写控件的初衷:
在日常的项目中,我们常常会用到一个按钮拥有两种点击状态,一种是pressed state,另外一种是unpressed state,通常我们会使用xml先写两个drawable xml文件:
(1)pressed state xml文件[android_shape_round_error_button_pressed.xml]:
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<corners android:radius="5dp"></corners>
<solid android:color="@color/error_button_pressed_color"></solid>
</shape>
(2)unpressed state xml文件[android_shape_round_error_button_unpressed.xml]:
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<corners android:radius="5dp"></corners>
<solid android:color="@color/error_button_unpressed_color"></solid>
</shape>
(3)利用上面两个xml最后写一个selctor xml 文件[android_error_button.xml]:
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@drawable/android_shape_round_error_button_pressed" android:state_pressed="true"></item>
<item android:drawable="@drawable/android_shape_round_error_button_unpressed" android:state_pressed="false"></item>
</selector>
(4)在自己页面xml控件属性中应用此selector背景:
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/android_error_button"/>
在以上的两个xml文件中,我们看似是很简单的操作,一旦项目中冗杂的代码按钮,你会每个都要写这样的代码,如果你只是追求吧项目完工之类的,于是就直接用个普通背景
代替了,这样你也就没必要来写自定义View了吧,Coder是一个动脑的行业,因此我们选择这一行业,就应该在项目中吸取应有的教训,怎么把下一次的项目更好、更快地去完成,
这样的代码自己写着舒服,别人接管的人看着也舒服,后期维护一方便。
控件思路:
此类控件无非就是先加载的时候默认有一个颜色,在点击事件或者触摸事件的时候就换了一种加深的颜色,我们不可能只想到我们做到
能换颜色就行了,因为即使你把两种颜色传递给你控件,那么你还要想这个颜色对应加深的颜色是什么颜色,没有必要,既然我们要
自定义控件,那么就一次性搞定控件,总之一句话,那就是我们怎么方便使用、怎么简单就怎么设计View。那么看似简单的控件有这么
几个问题:
(1)动态设置2种背景颜色
(2)默认颜色和点击颜色相似但是点击颜色要身些。
第一个问题其实不算什么问题,我们只要是控件都可以设置颜色,只是有一个问题的是,颜色处置问题。目前颜色拥有色调、饱和度
、色值。这种颜色变暗就只能在饱和度上面下功夫。饱和度在android有没有方法呢,我们开始只能猜测。于是我们可以在Android中寻找对应的类和方法,当然第一次想到的是Color
类,你可以先看饱和度对应的English:saturation,这时候你可以在Color类中find this word.你将会看着下面这几个类:
/**
* Convert the argb color to its HSV components.
* hsv[0] is Hue [0 .. 360)
* hsv[1] is Saturation [0...1]
* hsv[2] is Value [0...1]
* @param color the argb color to convert. The alpha component is ignored.
* @param hsv 3 element array which holds the resulting HSV components.
*/
public static void colorToHSV(@ColorInt int color, @Size(3) float hsv[]) {
RGBToHSV((color >> 16) & 0xFF, (color >> 8) & 0xFF, color & 0xFF, hsv);
}
此方法是将ARGB颜色转成HSV组件,在HSV组件中可以设置hsv[1]的饱和度,当然看到这,我们的目的还是没达到的,我们的
目的是调颜色的饱和度,那么饱和度已经调整了,那么肯定需要HSV组件转换成颜色。我们先猜测是HSVto...类似的方法搜索,
结果是就是这个方法下面,所有有时候别先急着搜,先看看需要的方法的附近代码。下面就是HSV组件转换成颜色方法:
/**
* Convert HSV components to an ARGB color. Alpha set to 0xFF.
* hsv[0] is Hue [0 .. 360)
* hsv[1] is Saturation [0...1]
* hsv[2] is Value [0...1]
* If hsv values are out of range, they are pinned.
* @param hsv 3 element array which holds the input HSV components.
* @return the resulting argb color
*/
public static int HSVToColor(@Size(3) float hsv[]) {
return HSVToColor(0xFF, hsv);
}
好了以上问题现在都不是问题了,现在是可以着手写自己的自定义View,此处的思路只是一种,我只是利用原有的控件的
属性加上原有的方法构造新控件,不想再draw,个人认为再次渲染没有原控件来的性能优越。
控件代码[ShapeButton]:
(1)attr.xml:
<!--ShapeButton-->
<attr name="shape" format="enum">
<enum name="rectangle" value="0"></enum>
<enum name="oval" value="1"></enum>
</attr>
<declare-styleable name="ShapeButton">
<attr name="solid_color" format="color" />
<attr name="stroke_color" format="color"/>
<attr name="stroke_width" format="dimension"/>
<attr name="corner_radius" format="dimension"/>
<attr name="top_left_corner_radius" format="dimension"/>
<attr name="top_right_corner_radius" format="dimension"/>
<attr name="bottom_left_corner_radius" format="dimension"/>
<attr name="bottom_right_corner_radius" format="dimension"/>
<attr name="shape"/>
<attr name="saturation" format="float"/>
</declare-styleable>
(2)ShapeButton源码:
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Color;
import android.graphics.PorterDuff;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.GradientDrawable;
import android.os.Build;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import com.android.R;
/**
* Created by Relin on 2017/11/22.
* This class is ButtonView you can setting background,stroke color
* stroke width ,stroke width,the kinds of corner in sample way.
* button color is change auto when click it and the color is old
* color's saturation 92% ,if you think isn't suit to you application
* you can change it,saturation is bright deep.
*/
public class ShapeButton extends TextView implements View.OnTouchListener{
private int solidColor = Color.parseColor("#B4B4B4");
private int strokeWidth = 0;
private int strokeColor = Color.parseColor("#B4B4B4");
private int shape = 0;
private float cornerRadius = 0;
private float topLeftCornerRadius = 0;
private float topRightCornerRadius = 0;
private float bottomLeftCornerRadius = 0;
private float bottomRightCornerRadius = 0;
private float saturation = 0.90f;
private Drawable normalDrawable;
private Drawable pressedDrawable;
public ShapeButton(Context context) {
super(context);
initAttrs(context,null);
}
public ShapeButton(Context context, AttributeSet attrs) {
super(context, attrs);
initAttrs(context,attrs);
}
@Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
setDrawable(pressedDrawable);
break;
case MotionEvent.ACTION_UP:
setDrawable(normalDrawable);
break;
}
return false;
}
private void initAttrs(Context context, AttributeSet attrs) {
setOnTouchListener(this);
if (attrs!=null){
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.ShapeButton);
solidColor = typedArray.getColor(R.styleable.ShapeButton_solid_color, Color.parseColor("#B4B4B4"));
strokeWidth = (int) typedArray.getDimension(R.styleable.ShapeButton_stroke_width, 0);
strokeColor = typedArray.getColor(R.styleable.ShapeButton_stroke_color, Color.parseColor("#B4B4B4"));
if (typedArray.getString(R.styleable.ShapeButton_shape)!=null){
shape = Integer.parseInt(typedArray.getString(R.styleable.ShapeButton_shape));
}
cornerRadius = typedArray.getDimension(R.styleable.ShapeButton_corner_radius, 0);
topLeftCornerRadius = typedArray.getDimension(R.styleable.ShapeButton_top_left_corner_radius, 0);
topRightCornerRadius = typedArray.getDimension(R.styleable.ShapeButton_top_right_corner_radius, 0);
bottomLeftCornerRadius = typedArray.getDimension(R.styleable.ShapeButton_bottom_left_corner_radius, 0);
bottomRightCornerRadius = typedArray.getDimension(R.styleable.ShapeButton_bottom_right_corner_radius, 0);
saturation = typedArray.getFloat(R.styleable.ShapeButton_saturation,0.90f);
typedArray.recycle();
}
normalDrawable = createShape(shape,strokeWidth, strokeColor, solidColor, cornerRadius, topLeftCornerRadius, topRightCornerRadius, bottomLeftCornerRadius, bottomRightCornerRadius);
pressedDrawable = createShape(shape, strokeWidth, createPressedColor(strokeColor), createPressedColor(solidColor), cornerRadius, topLeftCornerRadius, topRightCornerRadius, bottomLeftCornerRadius, bottomRightCornerRadius);
setDrawable(normalDrawable);
}
/**
* create pressed state color by HSV
* hsv[2] :values is big - is deep or bright
* @param color
* @return
*/
private int createPressedColor(int color) {
int alpha = Color.alpha(color);
float[] hsv = new float[3];
Color.colorToHSV(color, hsv);
hsv[2] *= saturation;
return Color.HSVToColor(alpha, hsv);
}
/**
* for all android api
* set all kinds of background.
* @param drawable
*/
private void setDrawable(Drawable drawable) {
if (Build.VERSION.SDK_INT >= 16) {
setBackground(drawable);
} else {
setBackgroundDrawable(drawable);
}
}
/**
* create shape drawable
* this method create you self background drawable
* by shape.the same as code in xml.
*
* @param shape GradientDrawable.RECTANGLE GradientDrawable.OVAL
* @param strokeWidth button stroke width
* @param strokeColor button stroke color
* @param solidColor button background color
* @param cornerRadius all corner is the same as is the radius
* @param topLeftCornerRadius top left corner radius
* @param topRightCornerRadius top right corner radius
* @param bottomLeftCornerRadius bottom left corner radius
* @param bottomRightCornerRadius bottom right corner radius
* @return
*/
public Drawable createShape(int shape, int strokeWidth,
int strokeColor, int solidColor, float cornerRadius,
float topLeftCornerRadius, float topRightCornerRadius,
float bottomLeftCornerRadius, float bottomRightCornerRadius) {
GradientDrawable drawable = new GradientDrawable();
drawable.setShape(shape);
drawable.setSize(10,10);
drawable.setStroke(strokeWidth, strokeColor);
drawable.setColor(solidColor);
if (cornerRadius!=0){
drawable.setCornerRadius(cornerRadius);
}else{
drawable.setCornerRadii(new float[]{topLeftCornerRadius, topLeftCornerRadius, topRightCornerRadius, topRightCornerRadius, bottomLeftCornerRadius, bottomLeftCornerRadius, bottomRightCornerRadius, bottomRightCornerRadius});
}
return drawable;
}
}
(3)使用:
按钮类型可以是圆形、正方形、椭圆形,shape的选择+View自身的宽高设置就可以了。其中saturation设置点击后的饱和度的。
<com.android.widget.ShapeButton
android:id="@+id/elven_fbtn_reloading"
android:layout_width="160dp"
android:layout_height="35dp"
android:layout_marginTop="15dp"
android:text="重新加载"
app:corner_radius="8dp"
android:textColor="@android:color/white"
android:textSize="14sp"
app:shape="rectangle"
android:gravity="center" />