本文全部是基于将双层界面修改成单层桌面的问题
最近客户提出要客制化定制Launcher3
对最近的修改做个笔记
1.修改默认壁纸
替换 frameworks/base/core/res/res/drawable-nodpi/default_wallpaper.jpg即可(注意找到对应的分辨率)
2.将双层桌面改成单层桌面
LauncherAppState.java
public static boolean isDisableAllApps() {
...
return true 即可
}
3.删除谷歌搜索
Launcher.java
public View getQsbBar() {
...
return null; 即可
}
注意上面会隐藏谷歌搜索,但是还会占着位置
想要不占位置
DeviceProfile.java
private void updateIconSize(float scale, int drawablePadding, Resources resources,
DisplayMetrics dm) {
...
// Search Bar
...
searchBarSpaceHeightPx = getSearchBarTopOffset()
+ resources.getDimensionPixelSize(R.dimen.dynamic_grid_search_bar_height);
将searchBarSpaceHeightPx=0;即可
}
Launcher里面好多东西都是动态计算的,所以这里计算为0就行
4.修改主屏幕大小位置 (即图标 几*几)
DynamicGrid.java
public DynamicGrid(Context context, Resources resources,
int minWidthPx, int minHeightPx,
int widthPx, int heightPx,
int awPx, int ahPx) {
DisplayMetrics dm = resources.getDisplayMetrics();
ArrayList<DeviceProfile> deviceProfiles =
new ArrayList<DeviceProfile>();
boolean hasAA = !LauncherAppState.isDisableAllApps();
DEFAULT_ICON_SIZE_PX = pxFromDp(DEFAULT_ICON_SIZE_DP, dm);
// Our phone profiles include the bar sizes in each orientation
deviceProfiles.add(new DeviceProfile("Super Short Stubby",
255, 300, 2, 3, 48, 13, (hasAA ? 3 : 5), 48, R.xml.default_workspace_4x4));
deviceProfiles.add(new DeviceProfile("Shorter Stubby",
255, 400, 3, 3, 48, 13, (hasAA ? 3 : 5), 48, R.xml.default_workspace_4x4));
/*chc edit*/
(客户要求2 * 2 ,找到对应的屏幕大小 修改 parameters 第4,5个即可 )
parameters 6:可以调整Icon的大小
parameters 8:控制hotseatd的数量
parameters 9:控制hotseatd的Icon大小
deviceProfiles.add(new DeviceProfile("Short Stubby",
275, 420, 2, 2, 70, 23, (hasAA ? 3 : 3), 53, R.xml.default_workspace_4x4));
/*chc end */
deviceProfiles.add(new DeviceProfile("Stubby",
255, 450, 2, 3, 48, 13, (hasAA ? 5 : 5), 48, R.xml.default_workspace_4x4));
deviceProfiles.add(new DeviceProfile("Nexus S",
296, 491.33f, 4, 4, 48, 13, (hasAA ? 5 : 5), 48, R.xml.default_workspace_4x4));
/// M: add 294 X 460 profile
deviceProfiles.add(new DeviceProfile("294 X 460",
294, 460, 3, 3, 50, 25, (hasAA ? 5 : 5), 59, R.xml.default_workspace_4x4));
deviceProfiles.add(new DeviceProfile("Nexus 4",
335, 567, 4, 4, DEFAULT_ICON_SIZE_DP, 13, (hasAA ? 5 : 5), 56, R.xml.default_workspace_4x4));
deviceProfiles.add(new DeviceProfile("Nexus 5",
359, 567, 4, 4, DEFAULT_ICON_SIZE_DP, 13, (hasAA ? 5 : 5), 56, R.xml.default_workspace_4x4));
deviceProfiles.add(new DeviceProfile("Large Phone",
406, 694, 5, 5, 64, 14.4f, 5, 56, R.xml.default_workspace_5x5));
// The tablet profile is odd in that the landscape orientation
// also includes the nav bar on the side
deviceProfiles.add(new DeviceProfile("Nexus 7",
575, 904, 5, 6, 72, 14.4f, 7, 60, R.xml.default_workspace_5x6));
// Larger tablet profiles always have system bars on the top & bottom
deviceProfiles.add(new DeviceProfile("Nexus 10",
727, 1207, 5, 6, 76, 14.4f, 7, 64, R.xml.default_workspace_5x6));
deviceProfiles.add(new DeviceProfile("20-inch Tablet",
1527, 2527, 7, 7, 100, 20, 7, 72, R.xml.default_workspace_4x4));
mMinWidth = dpiFromPx(minWidthPx, dm);
mMinHeight = dpiFromPx(minHeightPx, dm);
mProfile = new DeviceProfile(context, deviceProfiles,
mMinWidth, mMinHeight,
widthPx, heightPx,
awPx, ahPx,
resources);
}
5.禁止hotseat里面的Icon拖动,并且禁止形成文件夹
Launcher.java
public boolean onLongClick(View v) {
...
CellLayout.CellInfo longClickCellInfo = null;
View itemUnderLongClick = null;
if (v.getTag() instanceof ItemInfo) {
ItemInfo info = (ItemInfo) v.getTag();
longClickCellInfo = new CellLayout.CellInfo(v, info);;
itemUnderLongClick = longClickCellInfo.cell;
resetAddInfo();
}
/*chc add */ 在此处进行判断是否在hotseat进行长按
boolean isHotseatbutten=false;
if(longClickCellInfo!=null){
isHotseatbutten= longClickCellInfo.container==-100? false:true; //每一个Icon都有一个container的属性
//hostSeat:是-101 其他的是-100
}
if(isHotseatbutten){
return false;
}
/*chc end*/
final boolean inHotseat = isHotseatLayout(v); //不知为何原因 此isHotseatLayout(v)并不能判断时候在hotseat区域
...
return true;
}
禁止文件夹的形成
Workspace.java
boolean createUserFolderIfNecessary(View newView, long container, CellLayout target,
int[] targetCell, float distance, boolean external, DragView dragView,
Runnable postAnimationRunnable) {
...
/*chc add*/
if(container==-101){
return false;
}
/*chc end*/
...
}
6.将文件夹修改为4宫格
FolderIcon.java
1:修改圆形成为正方形
FolderIcon.xml
<com.android.launcher3.FolderIcon
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:focusable="true" >
<ImageView
android:id="@+id/preview_background"
android:layout_gravity="center_horizontal"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:antialias="true"
android:src="@drawable/portal_ring_inner_holo"/> <!--将src换成正方形图标即可-->
2.修改缩略图为2*2
// The number of icons to display in the
private static final int NUM_ITEMS_IN_PREVIEW = 4; //此处为文件夹显示缩略图的数量
// The amount of vertical spread between items in the stack [0...1]
private static final float PERSPECTIVE_SHIFT_FACTOR = 1.20f; //值越大 缩略图越小
private PreviewItemDrawingParams computePreviewItemDrawingParams(int index,
PreviewItemDrawingParams params) {
/*chc add */
int index_order = index;
final int previewPadding = FolderRingAnimator.sPreviewPadding;
/*chc end*/
// We want to imagine our coordinates from the bottom left, growing up and to the
// right. This is natural for the x-axis, but for the y-axis, we have to invert things.
float transY = mAvailableSpaceInPreview - (offset + scaledSize + scaleOffsetCorrection);
float transX = offset + scaleOffsetCorrection;
/*chc add */ 在此处计算图标的位置 , 下面是自己计算图标的位置
transX=1 * previewPadding +2;
transY=mAvailableSpaceInPreview - (mBaselineIconSize*2 + scaleOffsetCorrection)+previewPadding;
if(index_order==1){
transX=mBaselineIconSize+previewPadding*3;
transY=mAvailableSpaceInPreview - (mBaselineIconSize*2 + scaleOffsetCorrection)+previewPadding;
}else if(index_order==2){
transX=1 * previewPadding +2;
transY=mAvailableSpaceInPreview - (mBaselineIconSize + scaleOffsetCorrection)+previewPadding*2;
}else if(index_order==3){
transX=mBaselineIconSize+previewPadding*3;
transY=mAvailableSpaceInPreview - (mBaselineIconSize + scaleOffsetCorrection)+previewPadding*2;
}
/*chc end*/
return params;
}
public FolderRingAnimator(Launcher launcher, FolderIcon folderIcon) {
....
sSharedOuterRingDrawable = res.getDrawable(R.drawable.bg_rect);
sSharedInnerRingDrawable = res.getDrawable(R.drawable.bg_rect);
sSharedFolderLeaveBehind = res.getDrawable(R.drawable.bg_rect); //此处替换文件夹托动处形成动画的图片
...
}
}
7.Launcher修改成单层删除了google搜索无法显示删除APP按钮
问题3将google搜索return 成了null, 为了不占位置并且将他的高度修改成了0
所以导致了APP删除按钮的不显示
原因是google搜索包含了APP删除的按钮
所以现将问题3修改回来
Launcher.java
public View getQsbBar() {
...
if (widgetId != -1) {
if (true) { //chc edit isSmallLandTablet() to true 这个是判断searchView 的加载模式
// 改成true是默认让他直接加载不要动态加载
mQsbView = getLayoutInflater().inflate(R.layout.qsb, mSearchDropTargetBar, false);
..
} else {
mQsb = mAppWidgetHost.createView(this, widgetId, searchProvider);
}
///M.
}
}
修改qsb.xml
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:background="#00000000"> <!--chc eidt 修改背景为透明-->
<!-- Global search icon -->
<ImageView
android:id="@+id/search_button"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:scaleType="center"
android:visibility="gone" <!--chc eidt 隐藏-->
android:src="@drawable/ic_home_search_normal_holo"
android:adjustViewBounds="true" />
<!-- Voice search icon -->
<ImageView
android:id="@+id/voice_button"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:scaleType="center"
android:src="@drawable/ic_home_voice_search_holo"
android:adjustViewBounds="true"
android:visibility="gone" <!--chc eidt 隐藏-->
/>
</LinearLayout>
修改完成删除App按钮可能会有点大
为了整体美观
修改问题3的search的高度进行适当调整
此问题结束
8.将安装的APP拖动至删除APP按钮 无法显示删除对话框,并且删除后重启有会还原
原因是将安装的APP 进行删除流程的时候判断成了Weight,所以导致删不了,并且不能弹出删除对话框
修改Launcher的加载模式进行对安装的APP进行flag标记
LauncherModel.java
private boolean loadWorkspace(){
...
while (!mStopped && c.moveToNext()) {
...
if (info != null) {
//add by chc
if(intent.getAction() != null && intent.getAction().equals(Intent.ACTION_MAIN)){
try {
final String packageName = intent.getComponent().getPackageName();
//List<LauncherActivityInfoCompat> app = mLauncherApps.getActivityList(packageName, user);
PackageInfo pi = manager.getPackageInfo(packageName, 0);
if (!pi.applicationInfo.enabled) {
// If we return null here, the corresponding item will be removed from the launcher
// db and will not appear in the workspace.
//return null;
}
final int appFlags = manager.getApplicationInfo(packageName, 0).flags;
if ((appFlags & android.content.pm.ApplicationInfo.FLAG_SYSTEM) == 0) {
info.flags |= AppInfo.DOWNLOADED_FLAG;
if ((appFlags & android.content.pm.ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0) {
info.flags |= AppInfo.UPDATED_SYSTEM_APP_FLAG;
}
}
// info.firstInstallTime = manager.getPackageInfo(packageName, 0).firstInstallTime;
} catch (NameNotFoundException e) {
Log.d(TAG, "getPackInfo failed for componentName " + intent.getComponent());
//return null;
}
}
// chc add end
}
}
DeleteDropTarget.java
这里面是对删除的流程,在这里判断是否是安装的App,如果是安装的 就调用startApplicationUninstallActivity()进行删除
private void completeDrop(DragObject d) {
if (isAllAppsApplication(d.dragSource, item)) {
}
...
else if (isWorkspaceOrFolderApplication(d)) {
/ /chc add if the dragview is install app that this methed will uninstalled else the shutcut delete it
if(item.itemType != LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT){
ShortcutInfo shortcut = (ShortcutInfo) item;
final UserHandleCompat user = shortcut.user;
final ComponentName componentName = shortcut.intent.getComponent();
mLauncher.startApplicationUninstallActivity(componentName, shortcut.flags, user);
}else{
LauncherModel.deleteItemFromDatabase(mLauncher, item);
}
//chc add end
}
}
此问题到此结束
9.将hotSeat中间图标变大 两边的缩小
CellLayout.java
public boolean addViewToCellLayout(View child, int index, int childId, LayoutParams params,
boolean markCells) {
...
child.setScaleX(getChildrenScale());
child.setScaleY(getChildrenScale());
/*chc add */
if(mIsHotseat){
Log.d("cuihangchao","childId"+childId);
if(lp.cellX!=1){
child.setScaleX(0.8f);
child.setScaleY(0.8f);
}
}
/*chc end*/
// Generate an id for each view, this assumes we have at most 256x256 cells
...
return false;
}
在此处判断hotseat是否是Hotseat上面的图标
然后根据ID, 将需要变动的进行响应的缩放
这样修改完成之后还有坑
会将大图标拖动到小图标上,或进行大图渲染,无法变成小图。重启后会还原。
最终修改方案
ShortcutAndWidgetContainer.java
@Override
protected void dispatchDraw(Canvas canvas) {
@SuppressWarnings("all") // suppress dead code warning
final boolean debug = false;
/*chc add */
for (int i = getChildCount() - 1; i >= 0; i--) {
final View child = getChildAt(i);
final CellLayout.LayoutParams lps = (CellLayout.LayoutParams) child.getLayoutParams();
if(mIsHotseatLayout){
if(lps.cellX!=1){
child.setScaleX(0.7f);
child.setScaleY(0.7f);
}
}
}
/*chc end*/
if (debug) {
// Debug drawing for hit space
Paint p = new Paint();
p.setColor(0x6600FF00);
for (int i = getChildCount() - 1; i >= 0; i--) {
final View child = getChildAt(i);
final CellLayout.LayoutParams lp = (CellLayout.LayoutParams) child.getLayoutParams();
canvas.drawRect(lp.x, lp.y, lp.x + lp.width, lp.y + lp.height, p);
}
}
super.dispatchDraw(canvas);
}
拖动的时候会不停的进入此方法,
所以在此方法里面进行判断,
将不会影响到渲染的效果
改这个问题的时候踩了好多的坑。
哎,踩了好多的坑
此问题到此结束
大概上面的修改launcher 的样子
MTK 7.0 Launcher3修改单层桌面
2.1删除Google搜索(packages\apps\Launcher3\res\values\dimens.xml )
<dimen name="dynamic_grid_search_bar_height">6dp</dimen> //留下 (卸载 移除 的高度,因为搜索和他们是包含的)
Launcher.xml(注意找到对应分辨率)
<include
android:id="@+id/search_drop_target_bar"
android:background="#00000000"
layout="@layout/search_drop_target_bar"
android:visibility="gone"
/>
com/android/launcher3/SearchDropTargetBar
@Override
public void onDragStart(DragSource source, Object info, int dragAction) {
animateToState(State.DROP_TARGET, DEFAULT_DRAG_FADE_DURATION);
this.setVisibility(View.VISIBLE); //拖动的时候将自己显示
mQSB.setVisibility(View.GONE); //将QSB隐藏
}
@Override
public void onDragEnd() {
if (!mDeferOnDragEnd) {
animateToState(State.SEARCH_BAR, DEFAULT_DRAG_FADE_DURATION);
} else {
mDeferOnDragEnd = false;
}
this.setVisibility(View.GONE); //拖动结束的时候将自己隐藏
}
public void enableAccessibleDrag(boolean enable) {
if (mQSB != null) {
mQSB.setVisibility(View.GONE); //直接将qsb自己隐藏,
}
mInfoDropTarget.enableAccessibleDrag(enable);
mDeleteDropTarget.enableAccessibleDrag(enable);
mUninstallDropTarget.enableAccessibleDrag(enable);
}
删除移除按钮,单层桌面不需要移除
com/android/launcher3/DeleteDropTarget
@Override
protected boolean supportsDrop(DragSource source, Object info) {
if (info instanceof ShortcutInfo) {
ShortcutInfo item = (ShortcutInfo) info;
return item.itemType != LauncherSettings.BaseLauncherColumns.ITEM_TYPE_APPLICATION;
}
return info instanceof LauncherAppWidgetInfo;
//return source.supportsDeleteDropTarget() && supportsDrop(info);
}
到此,搜索就移除了,但是不影响拖动可卸载的应用进行卸载
2.2 将所有的应用加载到Workspace
/*chc add*/
public void customBindWorkspaceItems() {
final Context context = mApp.getContext();
ArrayList<ItemInfo> tmpInfos;
ArrayList<ItemInfo> added = new ArrayList<ItemInfo>();
synchronized (sBgLock) {
for (AppInfo app : mBgAllAppsList.data) {
tmpInfos = getItemInfoForComponentName(app.componentName, app.user);
if (tmpInfos.isEmpty()) {
// We are missing an application icon, so add this to the workspace
if(app.itemType!=LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT) { //不进行加载xml配置的Item
Log.d("Cuihangchao","app.title="+app.title);
added.add(app);
}
// This is a rare event, so lets log it
Log.e(TAG, "Missing Application on load: " + app);
}
}
if (!added.isEmpty()) {
addAndBindAddedWorkspaceItems(context,added); //添加到Workspace
}
}
}
LoaderTask的run()进行添加
public void run() {
synchronized (mLock) {
...
keep_running: {
if (DEBUG_LOADERS) Log.d(TAG, "step 1: loading workspace");
loadAndBindWorkspace();
if (mStopped) {
LauncherLog.d(TAG, "LoadTask break in the middle, this = " + this);
break keep_running;
}
waitForIdle();
// second step
if (DEBUG_LOADERS) Log.d(TAG, "step 2: loading all apps");
loadAndBindAllApps();
customBindWorkspaceItems();//chc add
}
3.新的应用安装增加到workspace
PackageUpdatedTask 的run
...
public void run() {
synchronized (mLock) {
if (added != null) {
final ArrayList<ItemInfo> addedInfos = new ArrayList<ItemInfo>(added); //chc add
addAndBindAddedWorkspaceItems(context, addedInfos);//chc add
addAppsToAllApps(context, added);
for (AppInfo ai : added) {
addedOrUpdatedApps.put(ai.componentName, ai);
}
}
...
}
到此,所有的应用都会加载到Workspace上面,不影响预制XML里面的应用进行重复加载
2.3删除Allapp按钮
void resetLayout() {
mContent.removeAllViewsInLayout();
if(false){ //chc add 大家可以自己定义宏
// Add the Apps button
Context context = getContext();
LayoutInflater inflater = LayoutInflater.from(context);
TextView allAppsButton = (TextView)
inflater.inflate(R.layout.all_apps_button, mContent, false);
Drawable d = context.getResources().getDrawable(R.drawable.all_apps_button_icon);
mLauncher.resizeIconDrawable(d);
int scaleDownPx = getResources().getDimensionPixelSize(R.dimen.all_apps_button_scale_down);
Rect bounds = d.getBounds();
d.setBounds(bounds.left, bounds.top + scaleDownPx / 2, bounds.right - scaleDownPx,
bounds.bottom - scaleDownPx / 2);
allAppsButton.setCompoundDrawables(null, d, null, null);
allAppsButton.setContentDescription(context.getString(R.string.all_apps_button_label));
allAppsButton.setOnKeyListener(new HotseatIconKeyEventListener());
if (mLauncher != null) {
mLauncher.setAllAppsButton(allAppsButton);
allAppsButton.setOnTouchListener(mLauncher.getHapticFeedbackTouchListener());
allAppsButton.setOnClickListener(mLauncher);
allAppsButton.setOnLongClickListener(mLauncher);
allAppsButton.setOnFocusChangeListener(mLauncher.mFocusHandler);
}
// Note: We do this to ensure that the hotseat is always laid out in the orientation of
// the hotseat in order regardless of which orientation they were added
int x = getCellXFromOrder(mAllAppsButtonRank);
int y = getCellYFromOrder(mAllAppsButtonRank);
CellLayout.LayoutParams lp = new CellLayout.LayoutParams(x,y,1,1);
lp.canReorder = false;
mContent.addViewToCellLayout(allAppsButton, -1, allAppsButton.getId(), lp, true);
}
}
//修复Bug。快速扔图标 图标变灰,并且不消失
DragController.java
public boolean onTouchEvent(MotionEvent ev) {
...
case MotionEvent.ACTION_UP:
// Ensure that we've processed a move event at the current pointer location.
handleMoveEvent(dragLayerX, dragLayerY);
mHandler.removeCallbacks(mScrollRunnable);
if (mDragging) {
//PointF vec = isFlingingToDelete(mDragObject.dragSource); //检测是否是扔图标,
PointF vec=null;// 删除扔图标操作,
if (!DeleteDropTarget.supportsDrop(mDragObject.dragInfo)) {
vec = null;
}
Log.d("Cuihangchao","vec="+vec);
if (vec != null) {
dropOnFlingToDeleteTarget(dragLayerX, dragLayerY, vec);
} else {
drop(dragLayerX, dragLayerY);
}
}
endDrag();
...
}
到此,7.0的Launcher3就改成单层的了
附8.1修改https://blog.csdn.net/qq_30552095
此文会持续更新,有做Launcher的朋友可以参考参考