Android Browser学习十一 框计算模块-Titlebar

好久没有更新博客了, 为了不让过去看的代码忘掉, 现在开始坚持更新. 看看之前写的博文, 虽然和大神还有有很大却别, 但是一路走来还是很佩服自己. 年轻真好!

废话少说, 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中 不同的功能在子类中, 并且也加载不同的布局, 甚至实现一点不同的功能. 给我们在适配过程中后很好的借鉴

Android Browser学习十一 框计算模块-Titlebar

然后看一下 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();
    }


你可能感兴趣的:(Android Browser学习十一 框计算模块-Titlebar)