一、搜索框/刪除框簡介
搜索框在手機桌面上方,當拖動一個快捷方式圖標時就會隱藏搜索框並且顯示刪除框.如下圖所示
二、搜索框/刪除框佈局
屏幕上方的搜索和刪除框是在一個佈局裏,名稱爲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:有需要交流的朋友,歡迎留言回覆.