时间:2020/12/29
之前公司不允许csdn,笔记写在其它地方。最近整理过来
需求描述
主界面google搜索栏显示在顶部,recentUI界面搜索栏显示在底部
需求解析:
1、google搜索栏默认的搜索栏移动到底部 https://blog.csdn.net/a396604593/article/details/129752909
2、修改google搜索栏显示在底部时的位置,隐藏掉底部的搜索栏。(这样看上去 主界面是不显示底部搜索的)
3、主界面顶部配置可卸载的默认布局google搜索栏
4、recentUI界面时位移hotseat的坐标,google搜索栏改为显示状态、recentUI界面其它view坐标调整
5、分屏模式下界面调整
需求实现
2、第二点的实现
vendor/partner_gms/apps/SearchLauncher/quickstep/src/com/android/searchlauncher/HotseatQsbWidget.java
@Override
public void setContentVisibility(int visibleElements, PropertySetter setter,
Interpolator interpolator) {
android.util.Log.d("setContentVisibility", "setContentVisibility visibleElements"+visibleElements);
boolean showAllAppsMode = (visibleElements & ALL_APPS_CONTENT) != 0;
//setter.setViewAlpha(mSearchWrapperView, showAllAppsMode ? 0 : 1, LINEAR);
setter.setViewAlpha(mFallbackSearchView, showAllAppsMode ? 1 : 0, LINEAR);
//通过判断launcher当前的状态 设置view的显示和隐藏
boolean showRecent = (visibleElements & (LauncherState.RECENTS_CLEAR_ALL_BUTTON)) != 0;
setter.setViewAlpha(mSearchWrapperView, showRecent ? 1 : 0, LINEAR);
}
由于主界面隐藏掉了搜索,我们需要修改<dimen name="dynamic_grid_hotseat_top_padding">0dp</dimen> 让hotseat底部看上去不会很大空隙
3、第三点的实现
默认布局文件添加
<appwidget
container="-100"
screen="0"
x="0"
y="0"
spanX="5"
spanY="1"
packageName="com.google.android.googlequicksearchbox"
className="com.google.android.googlequicksearchbox.SearchWidgetProvider"/>
关掉首页qsb
public static final boolean QSB_ON_FIRST_SCREEN = false;
4、第四点的实现
底边栏图标recentUI界面需要改变坐标的
4.1 需要位移的偏移量,在dimens.xml添加
<dimen name="dynamic_grid_hotseat_search_widget_height">30dp</dimen>
4.2 Java代码读取配置 , 添加变量
packages/apps/Launcher3/src/com/android/launcher3/DeviceProfile.java
public boolean isShowHotSeatSearchWidget = false;
public int hotSeatSearchWidgetHeightPx = 0;
...
hotSeatSearchWidgetHeightPx = res.getDimensionPixelSize(R.dimen.dynamic_grid_hotseat_search_widget_height);
4.3
读取配置之后,我们需要在恰当的时机改变 isShowHotSeatSearchWidget这个值,来判断是否需要添加偏移量
因为主要是对底边栏图标的偏移量,isShowHotSeatSearchWidget的控制我们放在packages/apps/Launcher3/src/com/android/launcher3/Hotseat.java
//Hotseat添加launcher状态改变监听
public class Hotseat extends CellLayout implements LogContainerProvider, Insettable, Transposable, LauncherStateManager.StateListener
...
//构造方法添加监听
//初始化位移坐标到内部变量,方便下面动画调用
public Hotseat(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
Launcher.getLauncher(getContext()).getStateManager().addStateListener(this);
mTranslationY = getTranslationY();
DeviceProfile grid = mActivity.getWallpaperDeviceProfile();
mTranslationYInRecentUi = mTranslationY - grid.hotSeatSearchWidgetHeightPx;
Log.d("Hotseat","mTranslationY: "+mTranslationY + " mTranslationYInRecentUi: "+mTranslationYInRecentUi);
}
//实现回调
@Override
public void onStateTransitionStart(LauncherState toState) {
//根据要切换到的状态,控制isShowHotSeatSearchWidget的改变
DeviceProfile grid = mActivity.getWallpaperDeviceProfile();
boolean oldBoolean = grid.isShowHotSeatSearchWidget;
if(toState == LauncherState.OVERVIEW){
grid.isShowHotSeatSearchWidget = true;
}else{
grid.isShowHotSeatSearchWidget = false;
}
//过滤掉不必要的状态,只有主界面和recentUI界面启动动画
if(toState !=LauncherState.OVERVIEW && toState != LauncherState.NORMAL){
return;
}
Log.d("Hotseat","oldBoolean: "+oldBoolean +" grid.isShowHotSeatSearchWidget: "+grid.isShowHotSeatSearchWidget);
//状态改变动画开始的时候,启动hotseat位移动画
if(oldBoolean != grid.isShowHotSeatSearchWidget){
AnimatorSet aSet = new AnimatorSet();
Animator a = getAnimator(grid.isShowHotSeatSearchWidget,Hotseat.this,View.TRANSLATION_Y);
View scrimView = Launcher.cast(mActivity).getScrimView();
Animator b = getAnimator(grid.isShowHotSeatSearchWidget,scrimView,View.TRANSLATION_Y);
aSet.setDuration(180);
aSet.play(a);
aSet.play(b);
startAnimation(aSet);
}
}
@Override
public void onStateTransitionComplete(LauncherState finalState) {
//类似于状态开始改变的逻辑,由于这里是装填改变完成的回调,不回去做动画,只是强制设置坐标点
//根据最终状态控制isShowHotSeatSearchWidget
DeviceProfile grid = mActivity.getWallpaperDeviceProfile();
if(finalState == LauncherState.OVERVIEW){
grid.isShowHotSeatSearchWidget = true;
}else{
grid.isShowHotSeatSearchWidget = false;
}
//过滤掉不必要的状态
if(finalState !=LauncherState.OVERVIEW && finalState != LauncherState.NORMAL){
return;
}
//setTranslationY
Log.d("Hotseat"," grid.isShowHotSeatSearchWidget: "+grid.isShowHotSeatSearchWidget);
Hotseat.this.setTranslationY(grid.isShowHotSeatSearchWidget?mTranslationYInRecentUi:mTranslationY);
//这个是主菜单界面的小箭头
View scrimView = Launcher.cast(mActivity).getScrimView();
scrimView.setTranslationY(grid.isShowHotSeatSearchWidget?mTranslationYInRecentUi:mTranslationY);
//适应分屏模式,调整间距
Rect padding = grid.getHotseatLayoutPadding();
if(!grid.isVerticalBarLayout()
&& grid.isMultiWindowMode
&& Launcher.getLauncher(getContext()).isInState(OVERVIEW)
){
padding.bottom = grid.hotSeatSearchWidgetHeightPx;
setPadding(padding.left, padding.top, padding.right, padding.bottom);
}
}
private float mTranslationY;
private float mTranslationYInRecentUi;
private AnimatorSet mCurrentAnimator;
private Animator getAnimator(boolean isShow,View view, Property property) {
return isShow
? ObjectAnimator.ofFloat(view, property, getTranslationY(), mTranslationYInRecentUi)
: ObjectAnimator.ofFloat(view, property, getTranslationY(), mTranslationY);
}
private void startAnimation(final AnimatorSet a) {
if (mCurrentAnimator != null && mCurrentAnimator.isRunning()) {
mCurrentAnimator.cancel();
}
a.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationStart(Animator animation) {
mCurrentAnimator = a;
}
@Override
public void onAnimationEnd(Animator animation) {
mCurrentAnimator = null;
}
});
a.start();
}
@Override
public void setInsets(Rect insets) {
FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) getLayoutParams();
DeviceProfile grid = mActivity.getWallpaperDeviceProfile();
insets = grid.getInsets();
if (grid.isVerticalBarLayout()) {
lp.height = ViewGroup.LayoutParams.MATCH_PARENT;
if (grid.isSeascape()) {
lp.gravity = Gravity.LEFT;
lp.width = grid.hotseatBarSizePx + insets.left;
} else {
lp.gravity = Gravity.RIGHT;
lp.width = grid.hotseatBarSizePx + insets.right;
}
} else {
lp.gravity = Gravity.BOTTOM;
lp.width = ViewGroup.LayoutParams.MATCH_PARENT;
lp.height = grid.hotseatBarSizePx + insets.bottom;
}
//适应分屏模式调整边距
Rect padding = grid.getHotseatLayoutPadding();
if(!grid.isVerticalBarLayout()
&& grid.isMultiWindowMode
&& Launcher.getLauncher(getContext()).isInState(OVERVIEW)
){
padding.bottom = grid.hotSeatSearchWidgetHeightPx;
}
setPadding(padding.left, padding.top, padding.right, padding.bottom);
setLayoutParams(lp);
rect.set(insets);
InsettableFrameLayout.dispatchInsets(this, insets);
}
4.4 其它view的坐标控制
recentUI界面我们需要位移一些其它的view
//本质上如果我们配置<dimen name="dynamic_grid_hotseat_top_padding">0dp</dimen>为30dp
//会导致主界面一直显示很大空隙
//所以我们参考使用这个值的所有地方,进行针对性调整,让他们加上上面动画偏移量
<dimen name="dynamic_grid_hotseat_top_padding">0dp</dimen>
packages/apps/Launcher3/quickstep/src/com/android/quickstep/util/LayoutUtils.java
使用dp.hotseatBarSizePx的地方, 减去dp.hotSeatSearchWidgetHeightPx
这里是recentUI界面,taskview相关的宽高测量计算,会影响recentUI界面view的位置和缩放比例
页面指示器位置调整
packages/apps/Launcher3/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java
setWorkspaceProperty中
//修改hotseat动画的Y坐标
if(state == LauncherState.OVERVIEW){
DeviceProfile grid = mLauncher.getWallpaperDeviceProfile();
hotseatScaleAndTranslation.translationY -= grid.hotSeatSearchWidgetHeightPx;
}
...
//hotseat和页面指示器的动画,这里按道理可以不改,双层模式已经不现实页面指示器了,单层才显示小圆点
propertySetter.setFloat(hotseat, View.TRANSLATION_Y,
hotseatScaleAndTranslation.translationY, hotseatTranslationInterpolator);
propertySetter.setFloat(mWorkspace.getPageIndicator(), View.TRANSLATION_Y,
hotseatScaleAndTranslation.translationY, hotseatTranslationInterpolator);
ScrimView 上滑箭头默认位置和动画过程中位置调整
packages/apps/Launcher3/src/com/android/launcher3/views/ScrimView.java
//因为之前客户需求,要这个指示器小箭头始终显示
//所以需要调整一下ScrimView的大小
//ScrimView是一个全屏的view,重写的ondraw方法自己画了一个箭头, 在上滑的时候,会有白色透明背景切换到allapps界面
//只在hotseat修改坐标, 上滑的时候底部会漏出来,界面显示异常.
//这里我们需要把ScrimView改成全屏+动画偏移量*2高度,保证它随手滑出allapps界面效果
//改变了整体高度,view的绘制位置也需要调整
//构造方法读取动画偏移量,方便内部使用
public ScrimView(Context context, AttributeSet attrs) {
super(context, attrs);
...
DeviceProfile grid = mLauncher.getWallpaperDeviceProfile();
mTranslationY = getTranslationY();
mTranslationYInRecentUi = mTranslationY - grid.hotSeatSearchWidgetHeightPx;
Log.d("ScrimView","setInsets mTranslationY: "+mTranslationY + " mTranslationYInRecentUi: "+mTranslationYInRecentUi);
}
//测量方法中,重新设置view的高度,保证只走一次
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
updateDragHandleBounds();
Log.d("ScrimView","mTranslationY: "+mTranslationY + " mTranslationYInRecentUi: "+mTranslationYInRecentUi);
int width = getMeasuredWidth();
int height = getMeasuredHeight();
Log.d("ScrimView"," height: "+height+" width: "+width);
ViewGroup.LayoutParams lp = getLayoutParams();
if(lp.height < height){
lp.height = height - (int)mTranslationYInRecentUi*2;
lp.width = width;
setLayoutParams(lp);
invalidate();
}
Log.d("ScrimView","lp : "+lp.height+" x "+lp.width+ " height: "+height+" width: "+width);
}
updateDragHandleBounds中更新绘制箭头的位置
left = (width - mDragHandleSize) / 2;
- topMargin = grid.hotseatBarSizePx;
+ topMargin = grid.hotseatBarSizePx - (int)mTranslationYInRecentUi*2;