在android13 默认使用了
我们想要将他改成圆形的图标,然后将字体修改到图标下方
首先我们要找到图标是在哪里添加进去的
在QSFactoryImpl有个方法
@Override
public QSTileView createTileView(Context context, QSTile tile, boolean collapsedView) {
QSIconView icon = tile.createTileView(context);
return new QSTileViewImpl(context, icon, collapsedView);
}
}
这里就是要添加图标的地方,现在的图标是QSTileViewImpl这个类实现的,我们要实现自己的肯定得给覆盖掉
@Override
public QSTileView createTileView(Context context, QSTile tile, boolean collapsedView) {
return new QSTileViewBak(context, icon, collapsedView);
}
这是我们新的实现,看下这个类的实现
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the
* License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/
package com.android.systemui.qs.tileimpl;
import android.content.Context;
import android.content.res.ColorStateList;
import android.content.res.Configuration;
import android.service.quicksettings.Tile;
import android.text.TextUtils;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import com.android.settingslib.Utils;
import com.android.systemui.FontSizeUtils;
import com.android.systemui.R;
import com.android.systemui.plugins.qs.QSIconView;
import com.android.systemui.plugins.qs.QSTile;
import java.util.Objects;
/** View that represents a standard quick settings tile. **/
public class QSTileViewBak extends QSTileBaseView {
private static final int MAX_LABEL_LINES = 2;
private static final boolean DUAL_TARGET_ALLOWED = false;
private View mDivider;
protected TextView mLabel;
protected TextView mSecondLine;
private ImageView mPadLock;
private int mState;
private ViewGroup mLabelContainer;
private View mExpandIndicator;
private View mExpandSpace;
private ColorStateList mColorLabelDefault;
private ColorStateList mColorLabelUnavailable;
public QSTileViewBak(Context context, QSIconView icon) {
this(context, icon, false);
}
public QSTileViewBak(Context context, QSIconView icon, boolean collapsedView) {
super(context, icon, collapsedView);
setClipChildren(false);
setClipToPadding(false);
setClickable(true);
setId(View.generateViewId());
createLabel();
setOrientation(VERTICAL);
setGravity(Gravity.CENTER_HORIZONTAL | Gravity.TOP);
mColorLabelDefault = Utils.getColorAttr(getContext(), android.R.attr.textColorPrimary);
// The text color for unavailable tiles is textColorSecondary, same as secondaryLabel for
// contrast purposes
mColorLabelUnavailable = Utils.getColorAttr(getContext(),
android.R.attr.textColorSecondary);
}
public TextView getLabel() {
return mLabel;
}
@Override
protected void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
FontSizeUtils.updateFontSize(mLabel, R.dimen.qs_tile_text_size_bak);
FontSizeUtils.updateFontSize(mSecondLine, R.dimen.qs_tile_text_size_bak);
}
@Override
public int getDetailY() {
android.util.Log.d("xsr_mLabel", "mLabelContainer.getHeight() / 2 = : "+mLabelContainer.getHeight() / 2);
return getTop() + mLabelContainer.getTop() + mLabelContainer.getHeight() / 2;
}
protected void createLabel() {
mLabelContainer = (ViewGroup) LayoutInflater.from(getContext())
.inflate(R.layout.qs_tile_label_bak, this, false);
android.util.Log.d("xsr_mLabel", "mLabelContainer = : "+mLabelContainer);
mLabelContainer.setClipChildren(false);
mLabelContainer.setClipToPadding(false);
mLabel = mLabelContainer.findViewById(R.id.tile_label_bak);
mPadLock = mLabelContainer.findViewById(R.id.restricted_padlock_bak);
mDivider = mLabelContainer.findViewById(R.id.underline_bak);
mExpandIndicator = mLabelContainer.findViewById(R.id.expand_indicator_bak);
mExpandSpace = mLabelContainer.findViewById(R.id.expand_space_bak);
mSecondLine = mLabelContainer.findViewById(R.id.app_label_bak);
addView(mLabelContainer,1);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
android.util.Log.d("xsr_mLabel", "mLabel.getLineCount() = : "+mLabel.getLineCount());
// Remeasure view if the primary label requires more then 2 lines or the secondary label
// text will be cut off.
if (mLabel.getLineCount() > MAX_LABEL_LINES || !TextUtils.isEmpty(mSecondLine.getText())
&& mSecondLine.getLineHeight() > mSecondLine.getHeight()) {
android.util.Log.d("xsr_mLabel", " onMeasure");
mLabel.setSingleLine();
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
}
@Override
protected void handleStateChanged(QSTile.State state) {
super.handleStateChanged(state);
if (!Objects.equals(mLabel.getText(), state.label) || mState != state.state) {
mLabel.setTextColor(state.state == Tile.STATE_UNAVAILABLE ? mColorLabelUnavailable
: mColorLabelDefault);
mState = state.state;
android.util.Log.d("xsr_mLabel", "mLabel = : "+state.label);
mLabel.setText(state.label);
mLabel.setVisibility(View.VISIBLE);
}
// if (!Objects.equals(mSecondLine.getText(), state.secondaryLabel)) {
// mSecondLine.setText(state.secondaryLabel);
// mSecondLine.setVisibility(TextUtils.isEmpty(state.secondaryLabel) ? View.GONE
// : View.VISIBLE);
// }
boolean dualTarget = DUAL_TARGET_ALLOWED && state.dualTarget;
mExpandIndicator.setVisibility(dualTarget ? View.VISIBLE : View.GONE);
mExpandSpace.setVisibility(dualTarget ? View.VISIBLE : View.GONE);
mLabelContainer.setContentDescription(dualTarget ? state.dualLabelContentDescription
: null);
if (dualTarget != mLabelContainer.isClickable()) {
mLabelContainer.setClickable(dualTarget);
mLabelContainer.setLongClickable(dualTarget);
mLabelContainer.setBackground(dualTarget ? newTileBackground() : null);
}
mLabel.setEnabled(!state.disabledByPolicy);
mPadLock.setVisibility(state.disabledByPolicy ? View.VISIBLE : View.GONE);
}
@Override
public void init(OnClickListener click, OnClickListener secondaryClick,
OnLongClickListener longClick) {
super.init(click, secondaryClick, longClick);
mLabelContainer.setOnClickListener(secondaryClick);
mLabelContainer.setOnLongClickListener(longClick);
mLabelContainer.setClickable(false);
mLabelContainer.setLongClickable(false);
}
}
他继承了一个类,我们也得实现
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the
* License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/
package com.android.systemui.qs.tileimpl;
import static com.android.systemui.qs.tileimpl.QSIconViewImpl.QS_ANIM_LENGTH;
import android.animation.ValueAnimator;
import android.content.Context;
import android.content.res.ColorStateList;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.drawable.AdaptiveIconDrawable;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.RippleDrawable;
import android.graphics.drawable.ShapeDrawable;
import android.graphics.drawable.shapes.PathShape;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.service.quicksettings.Tile;
import android.text.TextUtils;
import android.util.Log;
import android.util.PathParser;
import android.view.Gravity;
import android.view.View;
import android.view.ViewGroup;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityNodeInfo;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.Switch;
import com.android.settingslib.Utils;
import com.android.systemui.R;
import com.android.systemui.plugins.qs.QSIconView;
import com.android.systemui.plugins.qs.QSTile;
import com.android.systemui.plugins.qs.QSTile.BooleanState;
import android.graphics.drawable.shapes.OvalShape;
public class QSTileBaseView extends com.android.systemui.plugins.qs.QSTileView {
private static final String TAG = "QSTileBaseView";
private static final int ICON_MASK_ID = com.android.internal.R.string.config_icon_mask;
private final H mHandler = new H();
private final int[] mLocInScreen = new int[2];
private final FrameLayout mIconFrame;
protected QSIconView mIcon;
protected RippleDrawable mRipple;
private Drawable mTileBackground;
private String mAccessibilityClass;
private boolean mTileState;
private boolean mCollapsedView;
private boolean mShowRippleEffect = true;
private float mStrokeWidthActive;
private float mStrokeWidthInactive;
private final ImageView mBg;
private final int mColorActive;
private final int mColorInactive;
private final int mColorDisabled;
private int mCircleColor;
private int mBgSize;
int position ;
public QSTileBaseView(Context context, QSIconView icon) {
this(context, icon, false);
}
public QSTileBaseView(Context context, QSIconView icon, boolean collapsedView) {
super(context);
// Default to Quick Tile padding, and QSTileView will specify its own padding.
int padding = context.getResources().getDimensionPixelSize(R.dimen.qs_quick_tile_padding);
mIconFrame = new FrameLayout(context);
mStrokeWidthActive = context.getResources()
.getDimension(com.android.internal.R.dimen.config_qsTileStrokeWidthActive);
mStrokeWidthInactive = context.getResources()
.getDimension(com.android.internal.R.dimen.config_qsTileStrokeWidthInactive);
int size = context.getResources().getDimensionPixelSize(R.dimen.qs_quick_tile_size);
addView(mIconFrame, 0);
mBg = new ImageView(getContext());
Path path = new Path(PathParser.createPathFromPathData("M50 0A50 50,0,1,1,50 100A50 50,0,1,1,50 0"));
float pathSize = AdaptiveIconDrawable.MASK_SIZE;
PathShape p = new PathShape(path, pathSize, pathSize);
ShapeDrawable d = new ShapeDrawable(new OvalShape());
d.setTintList(ColorStateList.valueOf(Color.TRANSPARENT));
int bgSize = context.getResources().getDimensionPixelSize(R.dimen.qs_tile_background_size);
d.setIntrinsicHeight(bgSize);
d.setIntrinsicWidth(bgSize);
mBg.setImageDrawable(d);
FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(bgSize, bgSize, Gravity.CENTER);
mIconFrame.addView(mBg, lp);
mBg.setLayoutParams(lp);
mIcon = icon;
FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(
ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT,
Gravity.CENTER);
mIconFrame.addView(mIcon, params);
mIconFrame.setClipChildren(false);
mIconFrame.setClipToPadding(false);
mTileBackground = newTileBackground();
if (mTileBackground instanceof RippleDrawable) {
setRipple((RippleDrawable) mTileBackground);
}
setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES);
setBackground(mTileBackground);
mColorActive = context.getColor(R.color.pad_pro_blue);
mColorDisabled = Utils.getDisabled(context,
Utils.getColorAttrDefaultColor(context, android.R.attr.textColorTertiary));
mColorInactive = Utils.getColorAttrDefaultColor(context, R.attr.offStateColor);
setPadding(0, 0, 0, 0);
setClipChildren(false);
setClipToPadding(false);
mCollapsedView = collapsedView;
setFocusable(true);
}
@Override
public void setPosition(int position) {
// 你的方法实现
position = position;
}
public View getBgCircle() {
return mBg;
}
protected Drawable newTileBackground() {
final int[] attrs = new int[]{android.R.attr.selectableItemBackgroundBorderless};
final TypedArray ta = getContext().obtainStyledAttributes(attrs);
final Drawable d = ta.getDrawable(0);
ta.recycle();
return d;
}
private void setRipple(RippleDrawable tileBackground) {
mRipple = tileBackground;
if (getWidth() != 0) {
updateRippleSize();
}
}
private void updateRippleSize() {
// center the touch feedback on the center of the icon, and dial it down a bit
final int cx = mIconFrame.getMeasuredWidth() / 2 + mIconFrame.getLeft();
final int cy = mIconFrame.getMeasuredHeight() / 2 + mIconFrame.getTop();
final int rad = (int) (mIcon.getHeight() * .85f);
mRipple.setHotspotBounds(cx - rad, cy - rad, cx + rad, cy + rad);
}
@Override
public void init(QSTile tile) {
init(v -> tile.click(), v -> tile.secondaryClick(), view -> {
tile.longClick();
return true;
});
}
public void init(OnClickListener click, OnClickListener secondaryClick,
OnLongClickListener longClick) {
setOnClickListener(click);
setOnLongClickListener(longClick);
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
super.onLayout(changed, l, t, r, b);
if (mRipple != null) {
updateRippleSize();
}
}
@Override
public boolean hasOverlappingRendering() {
// Avoid layers for this layout - we don't need them.
return false;
}
/**
* Update the accessibility order for this view.
*
* @param previousView the view which should be before this one
* @return the last view in this view which is accessible
*/
public View updateAccessibilityOrder(View previousView) {
setAccessibilityTraversalAfter(previousView.getId());
return this;
}
public void onStateChanged(QSTile.State state) {
mHandler.obtainMessage(H.STATE_CHANGED, state).sendToTarget();
}
private void updateStrokeShapeWidth(QSTile.State state) {
Resources resources = getContext().getResources();
if (!(mBg.getDrawable() instanceof ShapeDrawable)) {
return;
}
ShapeDrawable d = (ShapeDrawable) mBg.getDrawable();
d.getPaint().setStyle(Paint.Style.FILL);
switch (state.state) {
case Tile.STATE_INACTIVE:
if (mStrokeWidthInactive >= 0) {
d.getPaint().setStyle(Paint.Style.STROKE);
d.getPaint().setStrokeWidth(mStrokeWidthInactive);
}
break;
case Tile.STATE_ACTIVE:
if (mStrokeWidthActive >= 0) {
d.getPaint().setStyle(Paint.Style.STROKE);
d.getPaint().setStrokeWidth(mStrokeWidthActive);
}
break;
}
}
protected void handleStateChanged(QSTile.State state) {
updateStrokeShapeWidth(state);
int circleColor = getCircleColor(state.state);
boolean allowAnimations = animationsEnabled();
if (circleColor != mCircleColor) {
if (allowAnimations) {
ValueAnimator animator = ValueAnimator.ofArgb(mCircleColor, circleColor)
.setDuration(QS_ANIM_LENGTH);
animator.addUpdateListener(animation -> mBg.setImageTintList(ColorStateList.valueOf(
(Integer) animation.getAnimatedValue())));
animator.start();
} else {
QSIconViewImpl.setTint(mBg, circleColor);
}
mCircleColor = circleColor;
}
mShowRippleEffect = state.showRippleEffect;
setClickable(state.state != Tile.STATE_UNAVAILABLE);
setLongClickable(state.handlesLongClick);
mIcon.setIcon(state, allowAnimations);
setContentDescription(state.contentDescription);
final StringBuilder stateDescription = new StringBuilder();
switch (state.state) {
case Tile.STATE_UNAVAILABLE:
stateDescription.append(mContext.getString(R.string.tile_unavailable));
break;
case Tile.STATE_INACTIVE:
if (state instanceof QSTile.BooleanState) {
stateDescription.append(mContext.getString(R.string.switch_bar_off));
}
break;
case Tile.STATE_ACTIVE:
if (state instanceof QSTile.BooleanState) {
stateDescription.append(mContext.getString(R.string.switch_bar_on));
}
break;
default:
break;
}
if (!TextUtils.isEmpty(state.stateDescription)) {
stateDescription.append(", ");
stateDescription.append(state.stateDescription);
}
setStateDescription(stateDescription.toString());
mAccessibilityClass =
state.state == Tile.STATE_UNAVAILABLE ? null : state.expandedAccessibilityClassName;
if (state instanceof QSTile.BooleanState) {
boolean newState = ((BooleanState) state).value;
if (mTileState != newState) {
mTileState = newState;
}
}
}
/* The view should not be animated if it's not on screen and no part of it is visible.
*/
protected boolean animationsEnabled() {
if (!isShown()) {
return false;
}
if (getAlpha() != 1f) {
return false;
}
getLocationOnScreen(mLocInScreen);
return mLocInScreen[1] >= -getHeight();
}
private int getCircleColor(int state) {
switch (state) {
case Tile.STATE_ACTIVE:
return mColorActive;
case Tile.STATE_INACTIVE:
case Tile.STATE_UNAVAILABLE:
return mColorDisabled;
default:
Log.e(TAG, "Invalid state " + state);
return 0;
}
}
@Override
public void setClickable(boolean clickable) {
super.setClickable(clickable);
setBackground(clickable && mShowRippleEffect ? mRipple : null);
}
@Override
public int getDetailY() {
return getTop() + getHeight() / 2;
}
public QSIconView getIcon() {
return mIcon;
}
public View getIconWithBackground() {
return mIconFrame;
}
@Override
public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
super.onInitializeAccessibilityEvent(event);
if (!TextUtils.isEmpty(mAccessibilityClass)) {
event.setClassName(mAccessibilityClass);
}
}
@Override
public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
super.onInitializeAccessibilityNodeInfo(info);
// Clear selected state so it is not announce by talkback.
info.setSelected(false);
if (!TextUtils.isEmpty(mAccessibilityClass)) {
info.setClassName(mAccessibilityClass);
if (Switch.class.getName().equals(mAccessibilityClass)) {
String label = getResources().getString(
mTileState ? R.string.switch_bar_on : R.string.switch_bar_off);
android.util.Log.d("xsr_mLabel", "label = : "+label);
// Set the text here for tests in
// android.platform.test.scenario.sysui.quicksettings. Can be removed when
// UiObject2 has a new getStateDescription() API and tests are updated.
info.setText(label);
info.setChecked(mTileState);
info.setCheckable(true);
if (isLongClickable()) {
info.addAction(
new AccessibilityNodeInfo.AccessibilityAction(
AccessibilityNodeInfo.AccessibilityAction
.ACTION_LONG_CLICK.getId(),
getResources().getString(
R.string.accessibility_long_click_tile)));
}
}
}
}
@Override
public String toString() {
final StringBuilder sb = new StringBuilder(getClass().getSimpleName()).append('[');
sb.append("locInScreen=(" + mLocInScreen[0] + ", " + mLocInScreen[1] + ")");
sb.append(", iconView=" + mIcon.toString());
sb.append(", tileState=" + mTileState);
sb.append("]");
return sb.toString();
}
private class H extends Handler {
private static final int STATE_CHANGED = 1;
public H() {
super(Looper.getMainLooper());
}
@Override
public void handleMessage(Message msg) {
if (msg.what == STATE_CHANGED) {
handleStateChanged((QSTile.State) msg.obj);
}
}
}
}
这两个类其实在android11有实现,我们只需要借鉴下,再给这两个文件编进去烧录后发现systemUi崩溃,查看发现animator有个空指针异常
diff --git a/src/com/android/systemui/qs/QSAnimator.java b/src/com/android/systemui/qs/QSAnimator.java
index f43d3ff6..e4b02f38 100644
--- a/src/com/android/systemui/qs/QSAnimator.java
+++ b/src/com/android/systemui/qs/QSAnimator.java
@@ -371,6 +374,7 @@ public class QSAnimator implements Callback, PageListener, Listener, OnLayoutCha
mQQSTileHeightAnimator.addView(quickTileView);
// Icons
+ Log.w("xsr_icon", "icons");
translateContent(
quickTileView.getIcon(),
tileView.getIcon(),
@@ -382,46 +386,46 @@ public class QSAnimator implements Callback, PageListener, Listener, OnLayoutCha
translationYBuilder,
qqsTranslationYBuilder
);
-
- // Label containers
- translateContent(
- quickTileView.getLabelContainer(),
- tileView.getLabelContainer(),
- view,
- xOffset,
- yOffset,
- mTmpLoc1,
- translationXBuilder,
- translationYBuilder,
- qqsTranslationYBuilder
- );
-
+ Log.w("xsr_icon", "labels");
+ // // Label containers
+ // translateContent(
+ // quickTileView.getLabelContainer(),
+ // tileView.getLabelContainer(),
+ // view,
+ // xOffset,
+ // yOffset,
+ // mTmpLoc1,
+ // translationXBuilder,
+ // translationYBuilder,
+ // qqsTranslationYBuilder
+ // );
+ Log.w("xsr_icon", "Secondary icon");
// Secondary icon
- translateContent(
- quickTileView.getSecondaryIcon(),
- tileView.getSecondaryIcon(),
- view,
- xOffset,
- yOffset,
- mTmpLoc1,
- translationXBuilder,
- translationYBuilder,
- qqsTranslationYBuilder
- );
+ // translateContent(
+ // quickTileView.getSecondaryIcon(),
+ // tileView.getSecondaryIcon(),
+ // view,
+ // xOffset,
+ // yOffset,
+ // mTmpLoc1,
+ // translationXBuilder,
+ // translationYBuilder,
+ // qqsTranslationYBuilder
+ // );
// Secondary labels on tiles not in QQS have two alpha animation applied:
// * on the tile themselves
// * on TileLayout
// Therefore, we use a quadratic interpolator animator to animate the alpha
// for tiles in QQS to match.
- quadraticInterpolatorBuilder
getRelativePosition(mTmpLoc1, qqsLayout, view);
mQQSTop = mTmpLoc1[1];
getRelativePosition(mTmpLoc2, tileView, view);
@@ -445,8 +452,8 @@ public class QSAnimator implements Callback, PageListener, Listener, OnLayoutCha
mOtherFirstPageTilesHeightAnimator.addView(tileView);
tileView.setClipChildren(true);
tileView.setClipToPadding(true);
- firstPageBuilder.addFloat(tileView.getSecondaryLabel(), "alpha", 0, 1);
- mAllViews.add(tileView.getSecondaryLabel());
+ // firstPageBuilder.addFloat(tileView.getSecondaryLabel(), "alpha", 0, 1);
+ // mAllViews.add(tileView.getSecondaryLabel());
}
mAllViews.add(tileView);
这样就可以实现圆形图标了,