Android4.4-Launcher源码分析系列之搜索框/删除框

一、搜索框/删除框简介

搜索框在手机桌面上方,当拖动一个快捷方式图标时就会隐藏搜索框并且显示删除框.如下图所示



二、搜索框/删除框布局

屏幕上方的搜索和删除框是在一个布局里,名称为qsb_bar,位于res/layout-port/launcher.xml布局里,如果你的设备是横屏的,那么在res/layout-land/launcher.xml布局里.

         <!--圆形指示器  -->
         <include
            android:id="@+id/page_indicator"
            layout="@layout/page_indicator"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center_horizontal" />  

        <!--搜索/删除框  -->
          <include
            android:id="@+id/qsb_bar"
            layout="@layout/qsb_bar" />  
可以看到,是用include加载了一个布局,点进去看

<com.android.launcher3.SearchDropTargetBar
    xmlns:android="http://schemas.android.com/apk/res/android"
    style="@style/QSBBar"
    android:focusable="false"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <!-- Drag specific targets container -->
    <LinearLayout
        style="@style/SearchDropTargetBar"
        android:id="@+id/drag_target_bar"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_gravity="center">

        <include
             layout="@layout/drop_target_bar" />
    </LinearLayout>
</com.android.launcher3.SearchDropTargetBar>

是一个自定义布局,类名为SearchDropTargetBar,后面再分析它。你会看到在这个布局里又include了一个布局,我们追究到底,点进去

<merge xmlns:android="http://schemas.android.com/apk/res/android">
    <FrameLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        style="@style/DropTargetButtonContainer"
        android:layout_weight="1">
        <!-- Delete target -->
        <com.android.launcher3.DeleteDropTarget
            style="@style/DropTargetButton"
            android:id="@+id/delete_target_text"
            android:text="@string/delete_zone_label_workspace"
            android:drawableStart="@drawable/remove_target_selector" />
    </FrameLayout>
    <FrameLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        style="@style/DropTargetButtonContainer"
        android:layout_weight="1">
        <!-- Info target -->
        <com.android.launcher3.InfoDropTarget
            style="@style/DropTargetButton"
            android:id="@+id/info_target_text"
            android:text="@string/info_target_label"
            android:drawableStart="@drawable/info_target_selector" />
    </FrameLayout>
</merge>
里面是两个FrameLayout布局,每个布局里都有一个自定义控件,上面一个是删除框,下面是一个显示app信息的框。你会发现这里没有搜索框啊。其实搜索框是动态添加的,在另一个布局里。在Launcher类的getQsBa方法里,加载了搜索框布局

/**
     * @return  获取搜索框和删除框
     */
    public View getQsbBar() {
        if (mQsbBar == null) {
            mQsbBar = mInflater.inflate(R.layout.search_bar, mSearchDropTargetBar, false);
            mSearchDropTargetBar.addView(mQsbBar);
        }
        return mQsbBar;
    }
原来它在R.layout.search_bar里。

三、搜索框/删除框代码分析

先分析Launcher类里关于搜索框/删除框的核心代码.在Launcher类的setupViews()方法里会把搜索框/删除框的布局加载进来

  

         // 获得搜索和删除框
        mSearchDropTargetBar = (SearchDropTargetBar) mDragLayer.findViewById(R.id.qsb_bar);
然后给搜索框传入Launcher实例和DragController实例   

       if (mSearchDropTargetBar != null) {
            mSearchDropTargetBar.setup(this, dragController);
        }

剩下的我们只要研究SearchDropTargetBar这个类就行。SearchDropTargetBar继承自FrameLayout并实现了DragController.DragListener接口.SearchDropTargetBar重写了onFinishInflate()方法,这个方法大家感到很陌生吧,这个方法是View的方法, 当View中的所有子控件映射成xml后触发,也就是说在Launcher类中的

        // 获得搜索和删除框
        mSearchDropTargetBar = (SearchDropTargetBar) mDragLayer.findViewById(R.id.qsb_bar);
执行完,就会回调SearchDropTargetBar的onFinishInflate()方法。我们看下这个onFinishInflate()方法.

 @Override
    protected void onFinishInflate() {
        super.onFinishInflate();

        // 包含删除和显示appinfo的布局
        mDropTargetBar = findViewById(R.id.drag_target_bar);
        //mDropTargetBar布局中的appinfo文字
        mInfoDropTarget = (ButtonDropTarget) mDropTargetBar.findViewById(R.id.info_target_text);
        //mDropTargetBar布局中的删除文字
        mDeleteDropTarget = (ButtonDropTarget) mDropTargetBar.findViewById(R.id.delete_target_text);

        mInfoDropTarget.setSearchDropTargetBar(this);
        mDeleteDropTarget.setSearchDropTargetBar(this);
        
        //是否增加位移动画,,默认值为false
        mEnableDropDownDropTargets =
            getResources().getBoolean(R.bool.config_useDropTargetDownTransition);
   
        // Create the various fade animations
        if (mEnableDropDownDropTargets) {
            LauncherAppState app = LauncherAppState.getInstance();
            DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
            mBarHeight = grid.searchBarSpaceHeightPx;
            mDropTargetBar.setTranslationY(-mBarHeight);
            mDropTargetBarAnim = LauncherAnimUtils.ofFloat(mDropTargetBar, "translationY",
                    -mBarHeight, 0f);
 
        } else {
            //一开始隐藏
        	mDropTargetBar.setAlpha(0f);
            //渐变动画,从无到有
            mDropTargetBarAnim = LauncherAnimUtils.ofFloat(mDropTargetBar, "alpha", 0f, 1f);  
        }
        //设置动画属性
        setupAnimation(mDropTargetBarAnim, mDropTargetBar);
    }

在这里获取一些控件实例,mDropTargetBar是包含删除和显示appinfo的布局,mInfoDropTarget则是文字appinfo,mDeleteDropTarget则是文字remove。接下来看mEnableDropDownDropTargets这个属性,它是解析values中的config得到的值,默认为false。它表示的含义是是否在隐藏搜索框和显示删除框的同时有向上下位移的动画。现在我们把它的值改为true,允许它有上下位移的动画。

与第一张图对比就增加了上下位移的动画。

往下看,如果不设置上下位移的动画,则进入到

 

            //一开始隐藏
        	mDropTargetBar.setAlpha(0f);
            //渐变动画,从无到有
            mDropTargetBarAnim = LauncherAnimUtils.ofFloat(mDropTargetBar, "alpha", 0f, 1f);  
这个就是设置渐变动画,先隐藏删除框,再创建一个删除框的渐变动画,我们看下声明

 /**
     * 删除框的动画
     */
    private ObjectAnimator mDropTargetBarAnim;
    /**
     * 搜索框的动画
     */
    private ObjectAnimator mQSBSearchBarAnim;
是属性动画.通过LauncherAnimUtils这个动画工具类来完成.

最后是设置动画属性,比如时长,增加监听等,在setupAnimation方法里.

删除框的动画知道了,那么搜索框的动画呢.前面已经分析了,Launche在加载mSearchDropTargetBar之后,会调用它的方法

    mSearchDropTargetBar.setup(this, dragController);
点击这个setup方法,进入到SearchDropTargetBar的setup方法,看下代码

public void setup(Launcher launcher, DragController dragController) {
        dragController.addDragListener(this);
        dragController.addDragListener(mInfoDropTarget);
        dragController.addDragListener(mDeleteDropTarget);
        dragController.addDropTarget(mInfoDropTarget);
        dragController.addDropTarget(mDeleteDropTarget);
        dragController.setFlingToDeleteDropTarget(mDeleteDropTarget);
        mInfoDropTarget.setLauncher(launcher);
        mDeleteDropTarget.setLauncher(launcher);
        mQSBSearchBar = launcher.getQsbBar();
        if (mEnableDropDownDropTargets) {
            mQSBSearchBarAnim = LauncherAnimUtils.ofFloat(mQSBSearchBar, "translationY", 0,
                    -mBarHeight);
        } else {
        	//渐变动画,从有到无
        	mQSBSearchBarAnim = LauncherAnimUtils.ofFloat(mQSBSearchBar, "alpha", 1f, 0f);
        }
        setupAnimation(mQSBSearchBarAnim, mQSBSearchBar);
    }
传入的Launcher和DragController实例添加监听等.重点看下面的,通过launcher.getQsbBar()获得搜索框实例,接下来就是和删除框一样的设置动画了.和删除框不同的是,这里的动画是有到无.

那么这些动画什么时候执行呢,前面说了SearchDropTargetBar实现了DragController.DragListener接口

   public class SearchDropTargetBar extends FrameLayout implements DragController.DragListener {
DragListener里有onDragStart和onDragEnd方法,对应开始拖动和拖动结束.那么我们来看SearchDropTargetBar的实现

      先看onDragStart

     /*
     * 在开始拖动时
     */
    @Override
    public void onDragStart(DragSource source, Object info, int dragAction) {
        // Animate out the QSB search bar, and animate in the drop target bar
    	Toast.makeText(getContext(), "SearchDropTargetBar.....191", 0).show(); 
    	prepareStartAnimation(mDropTargetBar);
        mDropTargetBarAnim.start();          //显示删除框
        if (!mIsSearchBarHidden) {
             prepareStartAnimation(mQSBSearchBar);
             mQSBSearchBarAnim.start();      //隐藏搜索框
        }
    }
prepareStartAnimation方法是对View的处理

 private void prepareStartAnimation(View v) {
         // 如果应用被硬加速了,view会被绘制到一个硬件纹理中.如果应用没被硬加速,此类型的layer的行为同于LAYER_TYPE_SOFTWARE
        v.setLayerType(View.LAYER_TYPE_HARDWARE, null);
    }
mDropTargetBarAnim.start();就是显示删除框,mIsSearchBarHidden是一个boolean值,表示是否隐藏搜索框,默认为false.如果为true,那么mQSBSearchBarAnim.start();隐藏搜索框.

接下来看下onDragEnd()

     /*
     * 拖动结束时
     */
    @Override
    public void onDragEnd() {
    	Toast.makeText(getContext(), "SearchDropTargetBar.....206", 0).show();
    	if (!mDeferOnDragEnd) {
            // Restore the QSB search bar, and animate out the drop target bar
            prepareStartAnimation(mDropTargetBar);
            mDropTargetBarAnim.reverse();
            if (!mIsSearchBarHidden) {
                prepareStartAnimation(mQSBSearchBar);
                mQSBSearchBarAnim.reverse();
            }
        } else {
            mDeferOnDragEnd = false;
        }
    }
就是对搜索框和删除框动画的还原.

有的业务需求是隐藏搜索框,那么直接把第三个参数改为0f就行. 

    //渐变动画,从有到无
        	mQSBSearchBarAnim = LauncherAnimUtils.ofFloat(mQSBSearchBar, "alpha", 0f, 0f);
如果需要自定义删除的样式,那么改变drop_target_bar布局就行了.

PS:有需要交流的朋友,欢迎留言回复.

     

  


      

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章