一.界面分析
主activity为LauncherActivity
主要为启动filesActivity
protected void onCreate(Bundle savedInstanceState) {
launch();
}
private void launch() {
ActivityManager activities = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
Intent intent = findTask(activities);
if (restoreTask(intent)) {
return;
} else {
// We failed to restore the task. It may happen when system was just updated and we
// moved the location of the targeted activity. Chances is that the rest of tasks
// can't be restored either, so clean those tasks and start a new one.
clearTask(activities);
}
startTask();
}
private void startTask() {
Intent intent = createLaunchIntent(this);//FilesActivity
startActivity(intent);
}
FilesActivity继承BaseActivity,先查看BaseActivity
super(R.layout.files_activity, TAG); //files_activity定义在layouts.xml中,实际为drawer_layout
public BaseActivity(@LayoutRes int layoutId, String tag) {
mLayoutId = layoutId; //files_activity
mTag = tag; //FilesActivity
}
public void onCreate(Bundle icicle) {
addListenerForLaunchCompletion(); //Closes the activity when it's idle
setContentView(mLayoutId);
setContainer();设置容器,实质设置coordinator_layout,container_save和container_roots
mInjector = getInjector();
mState = getState(icicle);
mDrawer = DrawerController.create(this, mInjector.config);
Metrics.logActivityLaunch(mState, intent);
mProviders = DocumentsApplication.getProvidersCache(this);
mDocs = DocumentsAccess.create(this);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
Breadcrumb breadcrumb =Shared.findView(this, R.id.dropdown_breadcrumb, R.id.horizontal_breadcrumb);
mNavigator = new NavigationViewManager
SearchManagerListener searchListener = new SearchManagerListener()
ViewGroup chipGroup = findViewById(R.id.search_chip_group);
mSearchManager = new SearchViewManager
mNavigator.setSearchBarClickListener(v -> {
mSearchManager.onSearchBarClicked();
mNavigator.update();
});
mSortController = SortController.create
mPreferencesMonitor.start();
}
//FilesActivity
public void onCreate(Bundle icicle) {
DocumentClipper clipper = DocumentsApplication.getDocumentClipper(this);
mInjector.selectionMgr = DocsSelectionHelper.create();
mInjector.focusManager = new FocusManager
mInjector.menuManager = new MenuManager
mInjector.actionModeController = new ActionModeController
mInjector.actions = new ActionHandler //AbstractActionHandler
mInjector.searchManager = mSearchManager
RootsFragment.show(getSupportFragmentManager(), null);
}
//布局drawer_layout
<androidx.coordinatorlayout.widget.CoordinatorLayout android:id="@+id/coordinator_layout">
<androidx.drawerlayout.widget.DrawerLayout android:id="@+id/drawer_layout">
<androidx.coordinatorlayout.widget.CoordinatorLayout android:orientation="vertical">
<FrameLayout>
<LinearLayout android:orientation="vertical">
<FrameLayout android:id="@+id/container_directory"/>
</LinearLayout>
<View android:id="@+id/drawer_edge"/>
</FrameLayout>
<androidx.coordinatorlayout.widget.CoordinatorLayout android:id="@+id/container_save" />
<include layout="@layout/directory_app_bar"/>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
<LinearLayout android:id="@+id/drawer_roots" android:orientation="vertical">
<androidx.appcompat.widget.Toolbar android:id="@+id/roots_toolbar"/>
<FrameLayout android:id="@+id/container_roots" />
</LinearLayout>
</androidx.drawerlayout.widget.DrawerLayout>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
//directory_app_bar
<com.google.android.material.appbar.AppBarLayout android:id="@+id/app_bar">
<com.google.android.material.appbar.CollapsingToolbarLayout android:id="@+id/collapsing_toolbar">
<include layout="@layout/directory_header"/>
<View android:id="@+id/toolbar_background_layout"/>
<androidx.appcompat.widget.Toolbar android:id="@+id/toolbar">
<TextView android:id="@+id/searchbar_title"/>
<com.android.documentsui.DropdownBreadcrumb android:id="@+id/dropdown_breadcrumb"/>
</androidx.appcompat.widget.Toolbar>
</com.google.android.material.appbar.CollapsingToolbarLayout>
</com.google.android.material.appbar.AppBarLayout>
//directory_header
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical">
<include layout="@layout/search_chip_row"/>
<include layout="@layout/apps_row"/>
<LinearLayout>
<TextView android:id="@+id/header_title">
<androidx.appcompat.widget.ActionMenuView android:id="@+id/sub_menu"/>
</LinearLayout>
<include layout="@layout/column_headers"/>
</LinearLayout>
//search_chip_row
<HorizontalScrollView>
<LinearLayout android:id="@+id/search_chip_group"/>
</HorizontalScrollView>
//apps_row
<LinearLayout android:id="@+id/apps_row" android:orientation="vertical">
<TextView android:text="@string/apps_row_title"/> //Browse files in other apps
<HorizontalScrollView>
<LinearLayout
android:id="@+id/apps_group"/>
</HorizontalScrollView>
</LinearLayout>
//column_headers
<merge />
//查看RootsFragment
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
mInjector = getBaseActivity().getInjector();
final View view = inflater.inflate(R.layout.fragment_roots, container, false);
mList = (ListView) view.findViewById(R.id.roots_list);
mList.setOnItemClickListener(mItemListener);
mList.setOnGenericMotionListener(
});
mList.setChoiceMode(ListView.CHOICE_MODE_SINGLE);
mList.setSelector(new ColorDrawable(Color.TRANSPARENT));
return view;
}
public static RootsFragment show(FragmentManager fm, Intent includeApps) {
final RootsFragment fragment = new RootsFragment();
ft.replace(R.id.container_roots, fragment);
return fragment;
}
public boolean onContextItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.root_menu_eject_root: //Eject
case R.id.root_menu_open_in_new_window: //Open in new window
case R.id.root_menu_paste_into_folder: //Paste into folder
case R.id.root_menu_settings: //Storage settings
}
}
//fragment_roots
<com.android.documentsui.sidebar.RootsList xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/roots_list"/>
二…侧边栏对应的fragment是什么
侧边栏对应的界面,点击侧边栏会调用onRootPicked,从打印可以知道均由DirectoryFragment来显示界面,Audio相关打印如下
04-25 09:10:21.131 3867 3867 D test : onRootPicked,root is Root{authority=com.android.providers.media.documents, rootId=audio_root, title=音频, isUsb=false, isSd=false, isMtp=false} @ content://com.android.providers.media.documents/root/audio_root
04-25 09:10:21.132 3867 3867 W ActionModeController: Tried to finish a null action mode.
04-25 09:10:21.132 3867 3867 D DocumentStack: Root changed to: Root{authority=com.android.providers.media.documents, rootId=audio_root, title=音频, isUsb=false, isSd=false, isMtp=false} @ content://com.android.providers.media.documents/root/audio_root
04-25 09:10:21.180 3867 3867 D DocumentStack: Adding doc to stack: DocumentInfo{docId=audio_root, name=音频, mimeType=vnd.android.document/directory, isContainer=true, isDirectory=true, isArchive=false, isInArchive=false, isPartial=false, isVirtual=false, isDeleteSupported=false, isCreateSupported=false, isMoveSupported=false, isRenameSupported=false, isMetadataSupported=false} @ content://com.android.providers.media.documents/document/audio_root
04-25 09:10:21.184 3867 3867 D DirectoryFragment: Showing directory: content://com.android.providers.media.documents/document/audio_root
04-25 09:10:21.184 3867 3867 D DirectoryFragment: Creating new fragment for directory: content://com.android.providers.media.documents/document/audio_root
04-25 09:10:21.306 3867 3867 D AbstractActionHandler: Creating new directory loader for: content://com.android.providers.media.documents/document/audio_root
04-25 09:10:21.345 3867 3867 D AbstractActionHandler: Loader has finished for: content://com.android.providers.media.documents/document/audio_root
04-25 09:10:21.345 3867 3867 I Model : Updating model with new result set. //更新cursor
04-25 09:10:21.346 3867 3867 D DirectoryFragment: Received model update. Loading=false
逻辑如下:
onRootPicked
-->refreshCurrentRootAndDirectory(AnimationView.ANIM_NONE);//传进的为1
-->refreshDirectory(anim); //对应AnimationView.ANIM_NON
-->DirectoryFragment.showDirectory(fm, root, cwd, anim);
-->create(fm, root, doc, anim);
public static void create(FragmentManager fm,RootInfo root,@Nullable DocumentInfo doc, @AnimationType int anim) {
args.putParcelable(Shared.EXTRA_ROOT, root);
args.putParcelable(Shared.EXTRA_DOC, doc);
final FragmentTransaction ft = fm.beginTransaction();
AnimationView.setupAnimations(ft, anim, args);
final DirectoryFragment fragment = new DirectoryFragment();
fragment.setArguments(args); //设置相关参数
ft.replace(getFragmentId(), fragment); //替代R.id.container_directory
ft.commitAllowingStateLoss();
}
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { /重点关注mRecView,为显示的列表,是一个recyclerview
final View view = inflater.inflate(R.layout.fragment_directory, container, false);
mRecView = (RecyclerView) view.findViewById(R.id.dir_list);
mRecView.setRecyclerListener( //回收
new RecyclerListener() {
@Override
public void onViewRecycled(ViewHolder holder) {
cancelThumbnailTask(holder.itemView);
}
});
mRecView.setItemAnimator(new DirectoryItemAnimator()); //设置添加、删除、移动、改变的动画效果
mRecView.setOnDragListener(mDragHoverListener); //拖拽滑动事件
return view;
}
public void onActivityCreated(Bundle savedInstanceState) {
mAdapter = new DirectoryAddonsAdapter(
mAdapterEnv,
new ModelBackedDocumentsAdapter(mAdapterEnv, mIconHelper, mInjector.fileTypeLookup)
);
mRecView.setAdapter(mAdapter);
mRecView.setLayoutManager(mLayout); //设置layout为GridLayout,
new ScaleHelper(this.getContext(), mInjector.features, this::scaleLayout) .attach(mRecView);
mActions.loadDocumentsForCurrentStack();
}
三.DirectoryFragment中mRecView的数据来自于哪
适配器DirectoryAddonsAdapter,主要逻辑如下
//传进的参数,mAdapterEnv为DirectoryFragment.java的子类AdapterEnvironment
//mIconHelper为IconHelper
//fileTypeLookup为<String, String> 的表
mAdapter = new DirectoryAddonsAdapter(mAdapterEnv, new ModelBackedDocumentsAdapter(mAdapterEnv, mIconHelper, mInjector.fileTypeLookup));
//构造方法
DirectoryAddonsAdapter(Environment environment, DocumentsAdapter delegate) {
mEnv = environment;
mDelegate = delegate;
mHeaderMessage = new HeaderMessage(environment, this::onDismissHeaderMessage);
mInflateMessage = new InflateMessage(environment, this::onDismissHeaderMessage);
mDelegate.registerAdapterDataObserver(new EventRelay());
mModelUpdateListener = this::onModelUpdate;
}
//onCreateViewHolder
public DocumentHolder onCreateViewHolder(ViewGroup parent, int viewType) {
default:
holder = mDelegate.createViewHolder(parent, viewType); //Audio界面对应的VierType为2,走default
}
return holder;
}
public void onBindViewHolder(DocumentHolder holder, int p, List<Object> payload) {
switch (holder.getItemViewType()) {
default:
mDelegate.onBindViewHolder(holder, toDelegatePosition(p), payload);//走default
break;
}
}
//ModelBackedDocumentsAdapter中的createViewHolders
四.整体逻辑
DirectoryLoader进行加载数据,DirectoryFragment进行界面的显示,DirectoryAddonsAdapter为界面的适配器
五.传进的参数root来源
为RootsFragment中的onLoadFinished的结果,来自RootsLoader,其监听com.android.documentsui.action.ROOT_CHANGED,由ProvidersCache中的doInBackground发送
而ProvidersCache中由RootsChangedObserver,采用ContentObserver机制监听数据库是否变化