好久没有更新博客了, 为了不让过去看的代码忘掉, 现在开始坚持更新. 看看之前写的博文, 虽然和大神还有有很大却别, 但是一路走来还是很佩服自己. 年轻真好!
废话少说, Browser的Titlebar功能还是比较全面的, 有进度条, 可以显示输入网址, 搜索关键词等, 可以显示搜索联想词等, 还有
停止, 刷新等按钮. 先看一下其布局: 可以看到他包括一个 NavigationBar 一个ProgressView两大部分
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/titlebar" android:layout_width="match_parent" android:layout_height="wrap_content"> <include layout="@layout/title_bar_nav" android:id="@+id/taburlbar" android:layout_width="match_parent" android:layout_height="@dimen/toolbar_height" /> <!-- 离线阅读时候的title --> <ViewStub android:id="@+id/snapshotbar_stub" android:layout="@layout/title_bar_snapshot" android:layout_width="match_parent" android:layout_height="@dimen/toolbar_height" /> <!--自动登录的title? --> <ViewStub android:id="@+id/autologin_stub" android:layout="@layout/title_bar_autologin" android:paddingTop="3dip" android:layout_below="@id/taburlbar" android:layout_width="match_parent" android:layout_height="wrap_content" /> <com.android.browser.PageProgressView android:id="@+id/progress" android:layout_width="match_parent" android:layout_height="wrap_content" android:background="@null" android:layout_below="@id/taburlbar" android:src="@drawable/progress" android:layout_marginTop="@dimen/progress_bar_margin" android:visibility="gone" /> </RelativeLayout>
Titlebar其实是一个RelativeLayout, 其初始化代码如下
private void initLayout(Context context) { LayoutInflater factory = LayoutInflater.from(context); factory.inflate(R.layout.title_bar, this); //把布局加载到自己身上 mProgress = (PageProgressView) findViewById(R.id.progress);//进度条 //这是显示网站信息的title 也就是navigationbar 定义了两个xml文件里面分别放了 //NavigationBarPhone 和NavigationBarTablet 通过base进行控制行为 mNavBar = (NavigationBarBase) findViewById(R.id.taburlbar); mNavBar.setTitleBar(this); }
其中 PageProgressView是继承Imageview自定义的一个View, 用来实现网页载入的进度更新 , 其实现很简单,
在setProgress的时候, 发一个消息出来, 通知界面进行刷新 , 注意 整个ProgressView不同于我们以前用过的ProgressBar
从代码可以看出, 即使一上来设置的progress 是 100, 他也会从0 慢慢移动到 100 而不是一下子变成100
/** *自定义了一个progressbar 用来显示载入网页进度 */ public class PageProgressView extends ImageView { public static final int MAX_PROGRESS = 10000; private static final int MSG_UPDATE = 42; private static final int STEPS = 10; private static final int DELAY = 40; private int mCurrentProgress; private int mTargetProgress; private int mIncrement; private Rect mBounds; private Handler mHandler; /** * @param context * @param attrs * @param defStyle */ public PageProgressView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); init(context); } /** * @param context * @param attrs */ public PageProgressView(Context context, AttributeSet attrs) { super(context, attrs); init(context); } /** * @param context */ public PageProgressView(Context context) { super(context); init(context); } private void init(Context ctx) { mBounds = new Rect(0,0,0,0); mCurrentProgress = 0; mTargetProgress = 0; mHandler = new Handler() { @Override public void handleMessage(Message msg) { if (msg.what == MSG_UPDATE) { mCurrentProgress = Math.min(mTargetProgress, mCurrentProgress + mIncrement); mBounds.right = getWidth() * mCurrentProgress / MAX_PROGRESS; invalidate(); if (mCurrentProgress < mTargetProgress) {//当前的Progress不断递增 sendMessageDelayed(mHandler.obtainMessage(MSG_UPDATE), DELAY); } } } }; } @Override public void onLayout(boolean f, int l, int t, int r, int b) { mBounds.left = 0; mBounds.right = (r - l) * mCurrentProgress / MAX_PROGRESS;// mBounds.top = 0; mBounds.bottom = b-t; //这是整个View的高度! } void setProgress(int progress) { //设置了Progress后发一个通知出去, 通知需要绘制的Bounds发生变化 mCurrentProgress = mTargetProgress; mTargetProgress = progress; mIncrement = (mTargetProgress - mCurrentProgress) / STEPS; mHandler.removeMessages(MSG_UPDATE); mHandler.sendEmptyMessage(MSG_UPDATE); } @Override public void onDraw(Canvas canvas) { // super.onDraw(canvas); Drawable d = getDrawable(); d.setBounds(mBounds); d.draw(canvas); } }
其使用
public void setProgress(int newProgress) { if (newProgress >= PROGRESS_MAX) { //当前进度 对于最大进度, setProgress后也会从0 走到 满格 mProgress.setProgress(PageProgressView.MAX_PROGRESS); mProgress.setVisibility(View.GONE); mInLoad = false; mNavBar.onProgressStopped(); // check if needs to be hidden if (!isEditingUrl() && !wantsToBeVisible()) { hide(); if (mUseQuickControls) { setShowProgressOnly(false); } } } else { if (!mInLoad) { mProgress.setVisibility(View.VISIBLE); mInLoad = true; mNavBar.onProgressStarted(); } //进度条步进到指定地方 mProgress.setProgress(newProgress * PageProgressView.MAX_PROGRESS / PROGRESS_MAX); if (!mShowing) { if (mUseQuickControls && !isEditingUrl()) {//如果是快速控制模式下,就只显示progressbar不显示title setShowProgressOnly(true); } show(); } } }
对于NavigationBarPhone 控件, 在pad ( layout-sw600dp) 和 phone(普通layout) 其实现分为了两套, 感觉做这个东西的人真的是个武林高手, 看上去很不对的地址栏, 在这个人的手里简单用一个继承实现了, 共用的功能在Base中 不同的功能在子类中, 并且也加载不同的布局, 甚至实现一点不同的功能. 给我们在适配过程中后很好的借鉴
然后看一下 NavigationBarBase.java的几个主要功能
初始化
/*这个函数是在xml中所有view都加载完了直接执行的*/ @Override protected void onFinishInflate() { super.onFinishInflate(); mLockIcon = (ImageView) findViewById(R.id.lock); mFavicon = (ImageView) findViewById(R.id.favicon); mUrlInput = (UrlInputView) findViewById(R.id.url); mUrlInput.setUrlInputListener(this); mUrlInput.setOnFocusChangeListener(this); mUrlInput.setSelectAllOnFocus(true); mUrlInput.addTextChangedListener(this); } public void setTitleBar(TitleBar titleBar) { mTitleBar = titleBar; mUiController = mTitleBar.getUiController(); mBaseUi = mTitleBar.getUi(); mUrlInput.setController(mUiController); }
UrlInputView是一个AutoInputTextv , 注册册焦点变化事件给了这个NavigationBarBase , 收到焦点变化后会做相应的处理:
@Override public void onFocusChange(View view, boolean hasFocus) { // if losing focus and not in touch mode, leave as is if (hasFocus || view.isInTouchMode() || mUrlInput.needsUpdate()) { setFocusState(hasFocus); } if (hasFocus) { mBaseUi.showTitleBar(); //在mUrlInput拿到焦点的时候打开输入法 mUrlInput.forceIme(); if (mInVoiceMode) { mUrlInput.forceFilter(); } } else if (!mUrlInput.needsUpdate()) { mUrlInput.dismissDropDown();//没有拿到焦点, 把下拉列表关掉 mUrlInput.hideIME(); if (mUrlInput.getText().length() == 0) { Tab currentTab = mUiController.getTabControl().getCurrentTab(); if (currentTab != null) { //如果edittext为空就显示标题 setDisplayTitle(currentTab.getUrl()); } } mBaseUi.suggestHideTitleBar(); } mUrlInput.clearNeedsUpdate(); }
两个消隐输入法的函数
/*强制显示输入法 该死的magic number!*/ void forceIme() { mInputManager.focusIn(this); mInputManager.showSoftInput(this, 0); } /*强制隐藏输入法*/ void hideIME() { mInputManager.hideSoftInputFromWindow(getWindowToken(), 0); }
至于mUrlInput 的其他功能, 我们会在后面的博客中做介绍
开始进行输入url, 关键词等的函数, 这个调用来自 外面的BaseUI等 用户想要进行输入的入口 , 如NavScreen的 打开输入标题栏
final NavTabView tabview = new NavTabView(mActivity); final Tab tab = getItem(position); tabview.setWebView(tab); mTabViews.put(tab, tabview.mImage); tabview.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) {//传入的view是用户实际点击的某个View, 如点击了标题栏 if (tabview.isClose(v)) { mScroller.animateOut(tabview); } else if (tabview.isTitle(v)) { switchToTab(tab); mUi.getTitleBar().setSkipTitleBarAnimations(true); close(position, false); mUi.editUrl(false); //最后跳转到startEditingUrl mUi.getTitleBar().setSkipTitleBarAnimations(false); } else if (tabview.isWebView(v)) { close(position); } } });
/** * called from the Ui when the user wants to edit * @param clearInput clear the input field */ void startEditingUrl(boolean clearInput) { // editing takes preference of progress setVisibility(View.VISIBLE); if (mTitleBar.useQuickControls()) { mTitleBar.getProgressView().setVisibility(View.GONE); } if (!mUrlInput.hasFocus()) { mUrlInput.requestFocus(); } if (clearInput) { mUrlInput.setText(""); } else if (mInVoiceMode) { mUrlInput.showDropDown(); } }
下面是NavigationBarPhone 了,
我们只看在phone 上实现的这一套, 也就是NavigationBarPhone.java
我们知道 NavigationBarPhone 不过是一个linearlayout , 其布局如下:
<com.android.browser.NavigationBarPhone xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="horizontal" android:background="@drawable/bg_urlbar"> <LinearLayout android:id="@+id/title_bg" android:layout_width="0dip" android:layout_weight="1.0" android:layout_height="match_parent" android:gravity="center_vertical" android:layout_marginLeft="8dip" android:layout_marginRight="8dip" android:orientation="horizontal"> <ImageView android:id="@+id/magnify" android:layout_width="wrap_content" android:layout_height="wrap_content" android:paddingLeft="4dip" android:paddingRight="8dip" android:visibility="gone" android:src="@drawable/ic_search_category_suggest" /> <ImageView android:id="@+id/incognito_icon" android:layout_width="wrap_content" android:layout_height="wrap_content" android:paddingLeft="4dip" android:visibility="gone" android:src="@drawable/ic_incognito_holo_dark" /> <FrameLayout android:id="@+id/iconcombo" android:layout_width="44dip" android:layout_height="match_parent" style="@style/HoloButton"> <ImageView android:id="@+id/favicon" android:layout_width="32dip" android:layout_height="32dip" android:paddingLeft="4dip" android:paddingRight="8dip" android:layout_gravity="center" /> <ImageView android:id="@+id/lock" android:layout_width="32dip" android:layout_height="32dip" android:layout_gravity="center" android:visibility="gone" /> </FrameLayout> <ImageView android:id="@+id/stop" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center_vertical" android:paddingLeft="4dip" android:contentDescription="@string/accessibility_button_stop" android:src="@drawable/ic_stop_holo_dark" style="@style/HoloButton" /> <com.android.browser.UrlInputView android:id="@+id/url" android:focusable="true" android:layout_width="0dip" android:layout_weight="1.0" android:layout_height="match_parent" android:fadingEdge="horizontal" android:fadingEdgeLength="24dip" android:textAppearance="?android:attr/textAppearanceMedium" android:hint="@string/search_hint" android:singleLine="true" android:ellipsize="end" android:lines="1" android:scrollHorizontally="true" android:inputType="textUri" android:imeOptions="actionGo|flagNoExtractUi|flagNoFullscreen" style="@style/Suggestions" android:background="@null" /> <ImageView android:id="@+id/voice" android:layout_width="wrap_content" android:layout_height="match_parent" android:paddingRight="4dip" android:contentDescription="@string/accessibility_button_voice" android:src="@drawable/ic_voice_search_holo_dark" style="@style/HoloButton" android:visibility="gone" /> <ImageView android:id="@+id/clear" android:layout_width="wrap_content" android:layout_height="match_parent" android:paddingRight="4dip" android:contentDescription="@string/accessibility_button_clear" android:src="@drawable/ic_close_window_holo_dark" style="@style/HoloButton" android:visibility="gone" /> </LinearLayout> <ImageButton android:id="@+id/tab_switcher" android:layout_width="wrap_content" android:layout_height="match_parent" android:contentDescription="@string/accessibility_button_navscreen" android:src="@drawable/ic_windows_holo_dark" style="@style/HoloButton" /> <ImageButton android:id="@+id/more" android:layout_width="wrap_content" android:layout_height="match_parent" style="@style/HoloButton" android:gravity="center_vertical" android:contentDescription="@string/accessibility_button_more" android:src="@drawable/ic_menu_overflow" /> </com.android.browser.NavigationBarPhone>
/** 初始化流程 * 在layout结束设置listener */ @Override protected void onFinishInflate() { super.onFinishInflate(); mStopButton = (ImageView) findViewById(R.id.stop); mStopButton.setOnClickListener(this); mVoiceButton = (ImageView) findViewById(R.id.voice); mVoiceButton.setOnClickListener(this); mClearButton = (ImageView) findViewById(R.id.clear); mClearButton.setOnClickListener(this); mMagnify = (ImageView) findViewById(R.id.magnify); mTabSwitcher = findViewById(R.id.tab_switcher); mTabSwitcher.setOnClickListener(this); mMore = findViewById(R.id.more); mMore.setOnClickListener(this); mComboIcon = findViewById(R.id.iconcombo); mComboIcon.setOnClickListener(this); mTitleContainer = findViewById(R.id.title_bg); setFocusState(false); Resources res = getContext().getResources(); mStopDrawable = res.getDrawable(R.drawable.ic_stop_holo_dark); mRefreshDrawable = res.getDrawable(R.drawable.ic_refresh_holo_dark); mStopDescription = res.getString(R.string.accessibility_button_stop); mRefreshDescription = res.getString(R.string.accessibility_button_refresh); mTextfieldBgDrawable = res.getDrawable(R.drawable.textfield_active_holo_dark); mUrlInput.setContainer(this); mUrlInput.setStateListener(this); mNeedsMenu = !ViewConfiguration.get(getContext()).hasPermanentMenuKey(); mIncognitoIcon = findViewById(R.id.incognito_icon); }
这个控件又有三种展示状态
/** * 由于phone的屏幕比较小所以把地址栏和输入栏做到了一起,然后用一个状态机进行管理 * 有三种状态 * 1.正常状态,显示网址和多窗口按钮以及网站图标 * 2.搜索状态,显示搜索的下拉列表 * 3.编辑状态,显示edittext 和 语音按钮 书签按钮 * * 这样的确定是父亲view上东西会很多 如果状态非常多可能不好管理 */ @Override public void onStateChanged(int state) { switch(state) { case StateListener.STATE_NORMAL: mComboIcon.setVisibility(View.VISIBLE); mStopButton.setVisibility(View.GONE); mClearButton.setVisibility(View.GONE); mMagnify.setVisibility(View.GONE); setSearchMode(mInVoiceMode); mTabSwitcher.setVisibility(View.VISIBLE); mTitleContainer.setBackgroundDrawable(null); mMore.setVisibility(mNeedsMenu ? View.VISIBLE : View.GONE); break; case StateListener.STATE_HIGHLIGHTED: mComboIcon.setVisibility(View.GONE); mStopButton.setVisibility(View.VISIBLE); mClearButton.setVisibility(View.GONE); mMagnify.setVisibility(View.GONE); setSearchMode(true); mTabSwitcher.setVisibility(View.GONE); mMore.setVisibility(View.GONE); mTitleContainer.setBackgroundDrawable(mTextfieldBgDrawable); break; case StateListener.STATE_EDITED: mComboIcon.setVisibility(View.GONE); mStopButton.setVisibility(View.GONE); mClearButton.setVisibility(View.VISIBLE); mMagnify.setVisibility(View.VISIBLE); setSearchMode(false); mTabSwitcher.setVisibility(View.GONE); mMore.setVisibility(View.GONE); mTitleContainer.setBackgroundDrawable(mTextfieldBgDrawable); break; } }
看上去Android 其实有一个自带的popupmenu 参考
http://6385551.blog.51cto.com/6375551/1163395
http://www.cnblogs.com/wolipengbo/p/3398603.html
/*显示menu菜单 */ void showMenu(View anchor) { Activity activity = mUiController.getActivity(); if (mPopupMenu == null) { mPopupMenu = new PopupMenu(mContext, anchor); mPopupMenu.setOnMenuItemClickListener(this); mPopupMenu.setOnDismissListener(this); if (!activity.onCreateOptionsMenu(mPopupMenu.getMenu())) { mPopupMenu = null; return; } } Menu menu = mPopupMenu.getMenu(); if (activity.onPrepareOptionsMenu(menu)) { mOverflowMenuShowing = true; mPopupMenu.show(); } }
/**
* A PopupMenu displays a {@link Menu} in a modal popup window anchored to a {@link View}.
* The popup will appear below the anchor view if there is room, or above it if there is not.
* If the IME is visible the popup will not overlap it until it is touched. Touching outside
* of the popup will dismiss it.
*/
在pad上
有一个显隐按钮 左边三个按钮的功能: 从左到右 移动出来, 使用了Animator:
private void hideNavButtons() { //滑走 int awidth = mNavButtons.getMeasuredWidth(); Animator anim1 = ObjectAnimator.ofFloat(mNavButtons, View.TRANSLATION_X, 0, - awidth); Animator anim2 = ObjectAnimator.ofInt(mUrlContainer, "left", mUrlContainer.getLeft(), mUrlContainer.getPaddingLeft()); Animator anim3 = ObjectAnimator.ofFloat(mNavButtons, View.ALPHA, 1f, 0f); AnimatorSet combo = new AnimatorSet(); combo.playTogether(anim1, anim2, anim3); combo.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { mNavButtons.setVisibility(View.GONE); } }); combo.setDuration(150); combo.start(); } private void showNavButtons() { //滑出来 int awidth = mNavButtons.getMeasuredWidth(); //Gone掉了还会有宽度吗?? Animator anim1 = ObjectAnimator.ofFloat(mNavButtons, View.TRANSLATION_X, -awidth, 0); Animator anim2 = ObjectAnimator.ofInt(mUrlContainer, "left", 0, awidth); Animator anim3 = ObjectAnimator.ofFloat(mNavButtons, View.ALPHA, 0f, 1f); AnimatorSet combo = new AnimatorSet(); combo.playTogether(anim1, anim2, anim3); mNavButtons.setVisibility(View.VISIBLE); combo.setDuration(150); combo.start(); }