修改皮肤
自定义View
「动态加载皮肤分析」
SharedPreferences
中;onStart
中对背景进行变化(也可以在设置背景的时候通过广播的方式及时修改另一个界面的背景)「自定义控件分析」
「Activity代码」
public class SkinChangeActivity extends Activity {
private Activity mContext;
private RatioColor mRatioColor;
private ViewGroup mLayout;
private SharedPreferencesUtil mSpu;
private static final int[] COLORS = new int[]{
Color.parseColor("#990000"),
Color.parseColor("#009900"),
Color.parseColor("#000099"),
Color.parseColor("#009999"),
Color.parseColor("#990099"),
Color.parseColor("#999900"),
Color.parseColor("#999999"),
Color.LTGRAY,
Color.RED,
Color.CYAN,
Color.DKGRAY,
Color.YELLOW,
Color.GREEN,
Color.BLACK,
Color.WHITE,
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mContext = this;
requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.activity_skin_change);
mSpu = SharedPreferencesUtil.getInstance(mContext);
initView();
}
private void initView() {
mLayout = (ViewGroup) findViewById(R.id.llyt_main);
if (mLayout == null) {
throw new NullPointerException("未找到视图");
}
mRatioColor = new RatioColor(mContext);
mRatioColor.addItems(COLORS);
mRatioColor.setOnCheckedColorListener(new RatioColor.onCheckedColorListener() {
@Override
public void doColor(int color) {
setCurrentBgColor(color);
}
});
mLayout.addView(mRatioColor);
// 设置当前背景色
setCurrentBgColor(mSpu.getSkin());
}
public void setCurrentBgColor(int color) {
mLayout.setBackgroundColor(color);
mRatioColor.setCheckedColor(color);
if (mSpu.getSkin() != color) {
mSpu.saveSkin(color);
}
}
}
「布局代码」activity_skin_change.xml
(只是一个LinearLayout布局)
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/llyt_main" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" android:padding="16dp"/>
「通用的自定义View源码」
public class RatioColor extends RadioGroup {
public interface onCheckedColorListener {
void doColor(int color);
}
private onCheckedColorListener mColorListener;
private final Context CONTEXT = getContext();
private final int W = Uscreen.dp2Px(CONTEXT, 48);
public RatioColor(Context context) {
this(context, null);
}
public RatioColor(Context context, AttributeSet attrs) {
super(context, attrs);
setUp();
}
private void setUp() {
// 设置默认宽高
setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, W));
// 设置默认样式(背景色,方向,重心)
setProperties(Color.parseColor("#eeeeee"), RadioGroup.HORIZONTAL, Gravity.START);
}
public void setProperties(int bgColor, int orientation, int gravity) {
setBackgroundColor(bgColor);
setOrientation(orientation);
setGravity(gravity);
ViewGroup.LayoutParams params = getLayoutParams();
// 在父布局里居左显示
if (params instanceof LinearLayout.LayoutParams) {
((LinearLayout.LayoutParams) params).gravity = Gravity.START;
}
// 动态更改方向
if (orientation == VERTICAL) {
params.width = W;
params.height = ViewGroup.LayoutParams.MATCH_PARENT;
} else {
params.width = ViewGroup.LayoutParams.MATCH_PARENT;
params.height = W;
}
}
/** * 设置颜色变化的监听 */
public void setOnCheckedColorListener(onCheckedColorListener listener) {
mColorListener = listener;
}
/** * 根据一组颜色值添加一组元素 */
public void addItems(int[] colorArray) {
for (int color : colorArray) {
addItem(color);
}
}
/** * 重新设置一组元素 */
public void resetItems(int[] colorArray) {
removeAllViews();
if (colorArray != null)
addItems(colorArray);
}
/** * 根据颜色值添加一个元素 * * @param color */
public void addItem(final int color) {
RadioButton rbtn = new RadioButton(CONTEXT);
rbtn.setFocusable(false);
rbtn.setFocusableInTouchMode(false);
rbtn.setLayoutParams(new RadioGroup.LayoutParams(W, W));
rbtn.setButtonDrawable(new ColorDrawable(Color.TRANSPARENT)); // 取消默认的样式
rbtn.setBackgroundResource(R.drawable.selector_skin_color); // 设置自己的样式(shape)
// 动态设置颜色值(基于selector)
StateListDrawable gradientDrawable = (StateListDrawable) rbtn.getBackground();
DrawableContainer.DrawableContainerState drawableContainerState = (DrawableContainer.DrawableContainerState) gradientDrawable.getConstantState();
Drawable[] children = drawableContainerState.getChildren();
for (Drawable child : children) {
if (child instanceof LayerDrawable) {
LayerDrawable layerDrawable = (LayerDrawable) child;
Drawable drawable = layerDrawable.getDrawable(0);
if (drawable instanceof GradientDrawable) {
GradientDrawable selectedDrawable = (GradientDrawable) drawable;
selectedDrawable.mutate(); // 此句不可少
selectedDrawable.setColor(color);
}
}
}
// 将颜色信息保存到Tag中
rbtn.setTag(color);
clickEffectByScaleAnim(rbtn, mOnCheckedChangeListener);
addView(rbtn);
}
/** * 设置某个颜色为选中状态 * * @param checkedColor */
public void setCheckedColor(int checkedColor) {
for (int i = 0; i < getChildCount(); i++) {
CompoundButton cbtn = (CompoundButton) getChildAt(i);
int color = (int) cbtn.getTag();
if (checkedColor == color) {
cbtn.setChecked(true);
}
}
}
/** * 获取当前选中的颜色 * * @return */
public int getCheckedColor() {
for (int i = 0; i < getChildCount(); i++) {
CompoundButton cbtn = (CompoundButton) getChildAt(i);
if (cbtn.isChecked()) {
return (int) cbtn.getTag();
}
}
return 0;
}
// 共用一个listener,避免多次创建消耗过多内存
private final CompoundButton.OnCheckedChangeListener mOnCheckedChangeListener = new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
if (isChecked) {
// 从Tag中取出color信息
int color = (int) buttonView.getTag();
if (mColorListener != null) {
mColorListener.doColor(color);
}
}
}
};
// -------------------- 滑动处理
private int mLastX;
private int mLastY;
private Scroller mScroller = new Scroller(CONTEXT);
@Override
public void computeScroll() {
super.computeScroll();
if (mScroller.computeScrollOffset()) {
scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
postInvalidate();
}
}
private void smoothScrollTo(int dextX) {
smoothScrollBy(dextX - getScrollX());
}
private void smoothScrollBy(int deltaX) {
mScroller.startScroll(getScrollX(), 0, deltaX, 0, 480);
invalidate();
}
@SuppressLint("ClickableViewAccessibility")
@Override
public boolean onTouchEvent(MotionEvent event) {
int x = (int) event.getX();
int y = (int) event.getY();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
mLastX = (int) event.getX();
break;
case MotionEvent.ACTION_MOVE:
int deltaX = x - mLastX;
int deltaY = y - mLastY;
scrollBy(-deltaX, 0);
break;
case MotionEvent.ACTION_UP:
// 松开后判断是否需要回弹
int scrollX = getScrollX();
int maxX = getChildCount() * W - getWidth();
if (scrollX < 0) {
smoothScrollTo(0);
} else if (scrollX > maxX) {
smoothScrollTo(maxX);
}
break;
}
mLastX = x;
mLastY = y;
return true;
}
// 滑动冲突处理
private float mLastXIntercepted;
private float mLastYIntercepted;
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
boolean intercepted = false;
int x = (int) ev.getX();
int y = (int) ev.getY();
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
intercepted = false;
break;
case MotionEvent.ACTION_MOVE:
// 移动的水平距离大于垂直距离时,进行拦截
intercepted = Math.abs(x - mLastXIntercepted) >= Math.abs(y - mLastYIntercepted) + 4;
break;
case MotionEvent.ACTION_UP:
intercepted = false;
break;
default:
break;
}
mLastX = x; // 此句不可少
mLastY = y;
mLastXIntercepted = x;
mLastYIntercepted = y;
return intercepted;
}
// ~~~~~~~~~~~~~~~~~~~~
// -------------------- 工具方法
public static void clickEffectByScaleAnim(final RadioButton radioButton,
RadioButton.OnCheckedChangeListener listener) {
// 设置监听
radioButton.setOnCheckedChangeListener(listener);
// 设置触摸效果
radioButton.setOnTouchListener(new OnTouchListener() {
@SuppressLint("ClickableViewAccessibility")
@Override
public boolean onTouch(View v, MotionEvent event) {
if (radioButton.isChecked()) { // 如果已经是选中状态,则不做任何效果处理;
return false;
}
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
doDown(v);
break;
case MotionEvent.ACTION_MOVE:
break;
case MotionEvent.ACTION_CANCEL:
doCancel(v);
break;
case MotionEvent.ACTION_UP:
doUp(v);
break;
}
return false;
}
// 按下时开始动画
private void doDown(View view) {
view.startAnimation(newAnimation(1f, 2f, 1000, true));
}
// 恢复视图
private void doUp(View view) {
view.clearAnimation();
view.startAnimation(newAnimation(0.4f, 1f, 400, false));
}
// 取消动画
private void doCancel(View view) {
view.clearAnimation();
}
});
}
public static Animation newAnimation(float from, float to, int duration, boolean repeat) {
Animation anim = new ScaleAnimation(from, to, from, to, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
anim.setDuration(duration);
anim.setFillAfter(false);
if (repeat) {
anim.setRepeatCount(Animation.INFINITE);
anim.setRepeatMode(Animation.REVERSE);
anim.setInterpolator(new BounceInterpolator());
}
return anim;
}
// ~~~~~~~~~~~~~~~~~~~~
}
「Selector代码」
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_checked="true">
<layer-list>
<item android:bottom="4dp" android:left="4dp" android:right="4dp" android:top="4dp">
<shape android:shape="oval">
<solid android:color="@android:color/transparent" />
</shape>
</item>
</layer-list>
</item>
<item android:state_checked="false">
<layer-list>
<item android:bottom="12dp" android:left="12dp" android:right="12dp" android:top="12dp">
<shape android:shape="oval">
<solid android:color="@android:color/transparent" />
</shape>
</item>
</layer-list>
</item>
</selector>
Android-onInterceptTouchEvent()和onTouchEvent()总结
以此类推,我们可以得到各种具体的情况,整个layout的view类层次中都有机会截获,而且能看出来外围的容器view具有优先截获权。
《Android开发艺术探索》第三章——View的滑动冲突