博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Android Browser学习七 书签历史模块: 书签UI的实现
阅读量:5790 次
发布时间:2019-06-18

本文共 12527 字,大约阅读时间需要 41 分钟。

hot3.png

浏览器的书签界面功能还是比较丰富的, 主要有

1.可以按照列表和grid两种方式展示

2.同步后会显示不同用户的书签

3.可以对书签进行拖拽

4.类似文件夹的树形层次, 类型window explorer 的地址管理

5.对书签的增删改查

UI如下

书签模块整体的时序图如下:

大致功能的类图如下

设计图有了, 就开始一步步分析把

1.大致启动流程:

书签的入口还是比较多的, 我们这里只拿从多窗口列表进入书签为例来介绍.从代码可以看到, 在点击了书签按钮之后调用了

mUiController.bookmarksOrHistoryPicker(ComboViews.Bookmarks);

这句代码. 正如以前所说, 所有的大功能都是通过controller来实现的 ,然后我们发现, 显示书签的View还是在controller的发起的:

/**     * Open the Go page. 打开 历史 书签 窗口     * @param startWithHistory If true, open starting on the history tab.     *                         Otherwise, start with the bookmarks tab.     */    @Override    public void bookmarksOrHistoryPicker(ComboViews startView) {        if (mTabControl.getCurrentWebView() == null) {            return;        }        // clear action mode        if (isInCustomActionMode()) {            endActionMode();        }        Bundle extras = new Bundle();        // Disable opening in a new window if we have maxed out the windows        extras.putBoolean(BrowserBookmarksPage.EXTRA_DISABLE_WINDOW,                !mTabControl.canCreateNewTab());        mUi.showComboView(startView, extras);    }

这个函数的作用其实是发起一个activity:

@Override    public void showComboView(ComboViews startingView, Bundle extras) {        Intent intent = new Intent(mActivity, ComboViewActivity.class);        intent.putExtra(ComboViewActivity.EXTRA_INITIAL_VIEW, startingView.name());        intent.putExtra(ComboViewActivity.EXTRA_COMBO_ARGS, extras);        Tab t = getActiveTab();        if (t != null) {            intent.putExtra(ComboViewActivity.EXTRA_CURRENT_URL, t.getUrl());        }        mActivity.startActivityForResult(intent, Controller.COMBO_VIEW);    }

ComboViewActivity是一个actionbar+ viewpager 生成的 的activity, viewpager有三个页面, 分别是书签, 离线网页, 和历史了:

他的指示器是一个actionbar

mViewPager = new ViewPager(this);        mViewPager.setId(R.id.tab_view);//可以这样指定一个id给java代码添加的view        setContentView(mViewPager); //这个view是一个viewpager        final ActionBar bar = getActionBar();        bar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);        if (BrowserActivity.isTablet(this)) {//如果是平面设置一下参数            bar.setDisplayOptions(ActionBar.DISPLAY_SHOW_HOME                    | ActionBar.DISPLAY_USE_LOGO);            bar.setHomeButtonEnabled(true);        } else {            bar.setDisplayOptions(0);        }        mTabsAdapter = new TabsAdapter(this, mViewPager); //把vierpager 和 actionbar关联        mTabsAdapter.addTab(bar.newTab().setText(R.string.tab_bookmarks),                BrowserBookmarksPage.class, args); //每个tab由 bar + fragment组成        mTabsAdapter.addTab(bar.newTab().setText(R.string.tab_history),                BrowserHistoryPage.class, args);        mTabsAdapter.addTab(bar.newTab().setText(R.string.tab_snapshots),                BrowserSnapshotPage.class, args);

这里我们只看BrowserBookmarksPage: 当这个page显示的时候首先执行的当然是onCreateView了:

@Override    public View onCreateView(LayoutInflater inflater, ViewGroup container,            Bundle savedInstanceState) {        mRoot = inflater.inflate(R.layout.bookmarks, container, false);        mEmptyView = mRoot.findViewById(android.R.id.empty);        mGrid = (BookmarkExpandableView) mRoot.findViewById(R.id.grid);        mGrid.setOnChildClickListener(this);        mGrid.setColumnWidthFromLayout(R.layout.bookmark_thumbnail);        mGrid.setBreadcrumbController(this);        setEnableContextMenu(mEnableContextMenu);//注册contextmenu        mDragHandler = new BookmarkDragHandler(getActivity(), mDragController,                mGrid.getDragAdapter());        // Start the loaders        LoaderManager lm = getLoaderManager();        lm.restartLoader(LOADER_ACCOUNTS, null, this);//把loader传入        return mRoot;    }

这个Page里面最主要的就是 BookmarkExpandableView, 这个view我们后面介绍, 想观察是如何填入数据的:

// Start the loaders        LoaderManager lm = getLoaderManager();        lm.restartLoader(LOADER_ACCOUNTS, null, this);//把loader传入

LoaderManger 这是高api版本引进的一个东西, 用于异步加载数据. 具体分析参考 

然后在 onCreateLoader 或返回cursor 来从数据库拿到书签信息 然后 在和 onLoadFinished中把获取的数据填入:

/*异步加载完成后就返回一个cursor我们就可以显示书签等操作了*/    @Override    public void onLoadFinished(Loader
loader, Cursor cursor) { if (loader.getId() == LOADER_ACCOUNTS) { LoaderManager lm = getLoaderManager(); int id = LOADER_BOOKMARKS; while (cursor.moveToNext()) { String accountName = cursor.getString(0); String accountType = cursor.getString(1); Bundle args = new Bundle(); args.putString(ACCOUNT_NAME, accountName); args.putString(ACCOUNT_TYPE, accountType); BrowserBookmarksAdapter adapter = new BrowserBookmarksAdapter( getActivity(), VIEW_THUMBNAILS); mBookmarkAdapters.put(id, adapter); boolean expand = true; try { expand = mState.getBoolean(accountName != null ? accountName : BookmarkExpandableView.LOCAL_ACCOUNT_NAME); } catch (JSONException e) {} // no state for accountName mGrid.addAccount(accountName, adapter, expand); lm.restartLoader(id, args, this); id++; } // TODO: Figure out what a reload of these means // Currently, a reload is triggered whenever bookmarks change // This is less than ideal // It also causes UI flickering as a new adapter is created // instead of re-using an existing one when the account_name is the // same. // For now, this is a one-shot load getLoaderManager().destroyLoader(LOADER_ACCOUNTS); } else if (loader.getId() >= LOADER_BOOKMARKS) { BrowserBookmarksAdapter adapter = mBookmarkAdapters.get(loader.getId()); adapter.changeCursor(cursor); } }

这样书签的信息就基本实现了. 

2. 我们可以看到, BookMarkPage最重要的Ui是 BookmarkExpandableView, 他的实现还是有些小复杂的, 分析一下他的类图:

原来他就是一个ExpandableListview, 这也和我们看到的东西是一样的, 其实这个书签的显示有两种展示风格:

a.类似上面UI的gridview的风格

b.类似小米浏览器书签的listview风格.

有UI上可以看出, 这些UI的实现都是在ExpandableView的getChildView的返回来实现的:

那么就从展示开始开始一步步分析吧:

1.Adapter的组装:  在onLoadfinished函数中: 载入数据ok之后, BookmarkExpandableView会按照不同的账户信息来添加书签:

mGrid.addAccount(accountName, adapter, expand);

添加的核心代码就是添加一个新的adapter

/**     * 添加账户后添加该用户的书签     * @param accountName     * @param adapter     * @param expandGroup     */    public void addAccount(String accountName, BrowserBookmarksAdapter adapter,            boolean expandGroup) {        // First, check if it already exists        int indexOf = mAdapter.mGroups.indexOf(accountName);               if (indexOf >= 0) {            //存在但是有更新            BrowserBookmarksAdapter existing = mAdapter.mChildren.get(indexOf);            if (existing != adapter) {                existing.unregisterDataSetObserver(mAdapter.mObserver);//先不要监听数据变化                // Replace the existing one 替换书签                mAdapter.mChildren.remove(indexOf);                mAdapter.mChildren.add(indexOf, adapter);                adapter.registerDataSetObserver(mAdapter.mObserver); //注册监听数据的变化            }        } else {            //没有添加这个用户的书签,            if (mCurrentView >= 0) {                //mCurrentView 标示  是list还是grid THUMBNAILS                adapter.selectView(mCurrentView);            }            mAdapter.mGroups.add(accountName);//添加用户名            mAdapter.mChildren.add(adapter); //添加            adapter.registerDataSetObserver(mAdapter.mObserver);        }        mAdapter.notifyDataSetChanged();        //添加后展开这个group         if (expandGroup) {            expandGroup(mAdapter.getGroupCount() - 1);        }    }

2.我们知道 在View 从添加到显示给用户会走onMeasure onLayout onDraw流程,

在onMeasure中会计算 一行可以容纳多少个书签:

/**         * 根据整个view的宽度来测量 一行可以容纳多少书签         * @param viewWidth         */        public void measureChildren(int viewWidth) {            if (mLastViewWidth == viewWidth) return;            int rowCount = viewWidth / mColumnWidth;            if (mMaxColumnCount > 0) {//不能超过最大的列数                rowCount = Math.min(rowCount, mMaxColumnCount);            }            int rowPadding = (viewWidth - (rowCount * mColumnWidth)) / 2;//设置 gridview的padding 使其水平居中            boolean notify = rowCount != mRowCount || rowPadding != mRowPadding; //是否发生了变化需要刷新, 会进行刷新view的操作 即 从adapter getView            mRowCount = rowCount;            mRowPadding = rowPadding;            mLastViewWidth = viewWidth;            if (notify) {                notifyDataSetChanged();            }        }

3.而这个ExpandableView在listview 显示的时候还需要getGroup和getChild:

/*填充childview ,                     注意每次调用这个函数:1.如果是list 模式 只能添加 一行 2.如果是grid 模式只能添加grid的一行 */        @Override        public View getChildView(int groupPosition, int childPosition,                boolean isLastChild, View convertView, ViewGroup parent) {            if (convertView == null) {                convertView = mInflater.inflate(R.layout.bookmark_grid_row, parent, false);            }            BrowserBookmarksAdapter childAdapter = mChildren.get(groupPosition);//拿到对应的adapter            int rowCount = mRowCount; //列数 / 一行有多少item            if (childAdapter.getViewMode() == BrowserBookmarksPage.VIEW_LIST) {                rowCount = 1; //如果是list就是一列            }            LinearLayout row = (LinearLayout) convertView;            if (row.getChildCount() > rowCount) {                row.removeViews(rowCount, row.getChildCount() - rowCount);//可能刷新界面 不显示已经删除的view            }            for (int i = 0; i < rowCount; i++) {//一个一个的添加view 注意,只添加 可以 看到的view                View cv = null;                if (row.getChildCount() > i) {                    cv = row.getChildAt(i);                }                int realChildPosition = (childPosition * rowCount) + i;                if (realChildPosition < childAdapter.getCount()) {                    View v = childAdapter.getView(realChildPosition, cv, row);                    v.setTag(R.id.group_position, groupPosition);//可以往tag 中放n个东西                    v.setTag(R.id.child_position, realChildPosition);                    v.setTag(R.id.child_id, childAdapter.getItemId(realChildPosition));                    v.setOnClickListener(mChildClickListener);                    v.setLongClickable(mLongClickable);                    if (mDragHandler != null) {                        v.setOnLongClickListener(mChildOnLongClickListener);                        //注册拖拽事件监听                        mDragHandler.registerBookmarkDragHandler(v);                    }                    if (cv == null) {                        row.addView(v);                    } else if (cv != v) {                        row.removeViewAt(i);                        row.addView(v, i);//更新新的view                    } else {                        cv.setVisibility(View.VISIBLE);                    }                } else if (cv != null) {                    cv.setVisibility(View.GONE);                }            }            return row;        }

也就是说, 每一行的数据是通过BrowserBookmarksAdapter的getView来产生view的:

这个东西继承了CursorAdapter , 应该是通过bindView来组装view的:

@Override    public void bindView(View view, Context context, Cursor cursor) {        if (mCurrentView == BrowserBookmarksPage.VIEW_LIST) {            bindListView(view, context, cursor);        } else {            bindGridView(view, context, cursor);        }    }

然后是getGroupView:

/*group的 view*/        @Override        public View getGroupView(int groupPosition, boolean isExpanded,                View view, ViewGroup parent) {            if (view == null) {                view = mInflater.inflate(R.layout.bookmark_group_view, parent, false);                view.setOnClickListener(mGroupOnClickListener);            }            view.setTag(R.id.group_position, groupPosition);            FrameLayout crumbHolder = (FrameLayout) view.findViewById(R.id.crumb_holder); //如果是二级菜单或更多 在gruopview上显示那个 上层目录            crumbHolder.removeAllViews();            BreadCrumbView crumbs = getBreadCrumbView(groupPosition); //如果是二级菜单 在gruopview上显示那个 上层目录            if (crumbs.getParent() != null) {//以前添加过 目录导航了 先删除以前的                ((ViewGroup)crumbs.getParent()).removeView(crumbs);            }            crumbHolder.addView(crumbs);            TextView name = (TextView) view.findViewById(R.id.group_name);            String groupName = mGroups.get(groupPosition);            if (groupName == null) {                groupName = mContext.getString(R.string.local_bookmarks);            }            name.setText(groupName);            return view;        }

这样就把各个账户的书签展示给用户了!

转载于:https://my.oschina.net/sfshine/blog/209723

你可能感兴趣的文章
v$archive_gap dg dataguard 断档处理 scn恢复
查看>>
问责IT风险管理:CIO需关注两个重点
查看>>
Winform打包发布图解
查看>>
PDF文件怎么编辑,超简单的方法
查看>>
EasyUI基础入门之Easyloader(载入器)
查看>>
Uva 839 Not so Mobile
查看>>
30款超酷的HTTP 404页面未找到错误设计
查看>>
程序猿必备 MyEclipse2013-2014系列
查看>>
java中ArrayList 、LinkList区别
查看>>
Spring ’14 Wave Update: Installing Dynamics CRM on Tablets for Windows 8.1
查看>>
利用rand7()构造rand10()
查看>>
MySQL 备份与恢复
查看>>
吃午饭前,按书上的代码写会儿--Hunt the Wumpus第一个版本
查看>>
easyui中combobox的值改变onchang事件
查看>>
Eclipse魔法堂:任务管理器
查看>>
一周自学动态站点设计
查看>>
Android-Universal-Image-Loader
查看>>
Android 从硬件到应用:一步一步向上爬 4 -- 使用 JNI 方法调硬件驱动
查看>>
TEST
查看>>
loadrunner 的Administration Page里面设置
查看>>