前言:
在Android日常开发过程中难免会遇到Android标准控件库中没有满足要求的,这时候就需要自定义控件。一个好的自定义控件应当和Android本身提供的控件一样,封装了一系列的功能以供开发者使用,不仅具有完备的功能,也优化对内存和CPU的使用。
指标:
个人自定义控件应该满足下面一些指标:
1. 应当遵守Android标准的规范(命名,可配置,事件处理等)。
2. 在XML布局中可配置控件的属性。
3. 对交互应当有合适的反馈,比如touch,click等。
4. 具有兼容性, Android版本很多,应该具有广泛的适用性。
好在Android已经提供了一系列基础控件和xml属性来帮助我们进行自定义控件的创建。
1.View的子类
在Android, 几乎所有的控件均继承自View,你也可以直接继承View也可以继承其他的控件比如ImageView等,也可能对这些控件进行组合。具体定义时需要至少提供一个构造函数,其中Context和AttributeSet作为参数,如下:
public LevelMenuItem(Context context, AttributeSet attrs) { super(context, attrs); }
2. 自定义属性
优秀的自定义控件需要支持可添加xml来配置属性和风格。 实现步骤如下:
1) Style XML 中添加自定义属性
2) 在xml的中,指定属性的名称与值类型
3) 在view中获取xml中的值
4) 将获取的值应用到view中
如sytle.xml中添加:
<declare-styleable name="LevelMenuItem"> <attr name="text" format="string" /> <attr name="text_color" format="color"/> <attr name="text_size" format="dimension" /> <attr name="image_src" format="reference"/> <attr name="image_bg" format="reference"/> <attr name="image_alpha" format="integer" /> <attr name="image_height" format="dimension"/> <attr name="image_width" format="dimension" /> declare-styleable>
layout中设置属性值:
<com.example.qinghua_liu.myapplication.LevelMenuItem android:id="@+id/item0" android:layout_below="@+id/mybutton1" android:layout_width="70dp" android:layout_height="80dp" linc:text="杭州" linc:text_size="6sp" linc:text_color="#80fa8072" linc:image_src="@drawable/orange_button_selector" linc:image_alpha="128" linc:image_height="48dp" linc:image_width="48dp" />
3. 应用属性值
之后 在自定义控件中,就可以读这些值,并应用到控件上了,需要注意的是, TypeArray使用完毕后需要销毁,不然会发生内存泄露
mTextView = (TextView) findViewById(R.id.tv_item); String textString = typedArray.getString(R.styleable.LevelMenuItem_text); int textColor = typedArray.getColor(R.styleable.LevelMenuItem_text_color, 0xffffffff); float textSize = typedArray.getDimension(R.styleable.LevelMenuItem_text_size, 20); mTextView.setText(textString); mTextView.setTextColor(textColor); mTextView.setTextSize(textSize);4. 设置方法与事件回调 自定义它只能在view初始化的时候被应用到控件中。 为了添加更加灵活的行为,就需要自定义方法:
public int getTextSize() { return this.mTextSize; }
public void setTextSize(int textSize) {
this.mTextSize=textSize;
invalidate();
requestLayout();
}
invalidate()和requestLayout(), 保证了view能及时的更新。在你的自定义View中,如果有属性被改变并且需要立即生效时,
就需要调用这个方法。 这样系统会立即重新绘制view。 同样的,如果view的尺寸或者形状发生了变化,也必须调用requestLayout()。
实战
下面结合代码来尝试自定义组合控件的实现:
素材:
找两张图片素材,做一个圆形的按钮,一张正常显示,一点按下时显示:
Style属性:
<declare-styleable name="LevelMenuItem"> <attr name="text" format="string" /> <attr name="text_color" format="color"/> <attr name="text_size" format="dimension" /> <attr name="image_src" format="reference"/> <attr name="image_bg" format="reference"/> <attr name="image_alpha" format="integer" /> <attr name="image_height" format="dimension">attr> <attr name="image_width" format="dimension" /> declare-styleable>
定义ImageView selector xml
drawable/selector
xml version="1.0" encoding="utf-8"?> <selector xmlns:android="http://schemas.android.com/apk/res/android" > <item android:state_pressed="true" android:drawable="@drawable/button_push"/> <item android:drawable="@drawable/orange_button"/> selector>
layout:
level_menu_item.xml(图片加文字的组合按钮)
xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="fill_parent" android:gravity="center" android:orientation="vertical" > <ImageView android:layout_gravity="center_horizontal" android:id="@+id/image_item" android:layout_width="fill_parent" android:layout_height="fill_parent" android:scaleType="fitCenter" /> <TextView android:id="@+id/tv_item" android:layout_width="fill_parent" android:layout_height="wrap_content" android:gravity="center_horizontal" android:textColor="#23ffffff" android:textSize="25sp" android:layout_gravity="center_horizontal" /> LinearLayout>
level_menu.xml:(控件组,包含三个上面的控件)
xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:linc="http://schemas.android.com/apk/res-auto" android:layout_width="fill_parent" android:layout_height="fill_parent" android:orientation="horizontal"> <com.example.qinghua_liu.myapplication.LevelMenuItem android:id="@+id/item1" android:layout_width="70dp" android:layout_height="80dp" linc:text="天气" linc:text_size="6sp" linc:text_color="#80fa8072" linc:image_src="@drawable/orange_button_selector" linc:image_alpha="128" linc:image_height="48dp" linc:image_width="48dp" /> <com.example.qinghua_liu.myapplication.LevelMenuItem android:id="@+id/item2" android:layout_marginLeft="20dp" android:layout_width="70dp" android:layout_height="80dp" linc:text="新闻" linc:text_size="6sp" linc:text_color="#ffeee8aa" linc:image_src="@drawable/orange_button_selector" linc:image_alpha="255" linc:image_height="48dp" linc:image_width="48dp" /> <com.example.qinghua_liu.myapplication.LevelMenuItem android:id="@+id/item3" android:layout_marginLeft="20dp" android:layout_width="70dp" android:layout_height="80dp" linc:text="景区" linc:text_size="6sp" linc:text_color="#80cd853f" linc:image_src="@drawable/orange_button_selector" linc:image_alpha="128" linc:image_height="48dp" linc:image_width="48dp" /> LinearLayout>
LevelMenuItem.java
public class LevelMenuItem extends LinearLayout { private TextView mTextView = null; private ImageView mImageView = null; public LevelMenuItem(Context context) { super(context); } public LevelMenuItem(Context context, AttributeSet attrs) { super(context, attrs); LayoutInflater layoutInflater = (LayoutInflater) context. getSystemService(Context.LAYOUT_INFLATER_SERVICE); layoutInflater.inflate(R.layout.level_menu_item, this); TypedArray typedArray = context.obtainStyledAttributes(attrs , R.styleable.LevelMenuItem); initWidget(typedArray); } private void initWidget(TypedArray typedArray) { mTextView = (TextView) findViewById(R.id.tv_item); String textString = typedArray.getString(R.styleable.LevelMenuItem_text); int textColor = typedArray.getColor(R.styleable.LevelMenuItem_text_color, 0xffffffff); float textSize = typedArray.getDimension(R.styleable.LevelMenuItem_text_size, 20); mTextView.setText(textString); mTextView.setTextColor(textColor); mTextView.setTextSize(textSize); mImageView = (ImageView) findViewById(R.id.image_item); int imageHeight = (int) typedArray.getDimension(R.styleable.LevelMenuItem_image_height, 25); int imageWidth = (int) typedArray.getDimension(R.styleable.LevelMenuItem_image_width, 25); int imageSrc = typedArray.getResourceId(R.styleable.LevelMenuItem_image_src, 0); int imageBg = typedArray.getResourceId(R.styleable.LevelMenuItem_image_bg, 0); int imageAlpha = typedArray.getInt(R.styleable.LevelMenuItem_image_alpha, 255); mImageView.setAlpha(imageAlpha); mImageView.setImageResource(imageSrc); mImageView.setBackgroundResource(imageBg); LayoutParams layoutParams = new LayoutParams(imageWidth, imageHeight); mImageView.setLayoutParams(layoutParams); typedArray.recycle(); } /** * 设置此控件的文本 * * @param text */ public void setText(String text) { mTextView.setText(text); } /** * 设置文字颜色 * * @param textColor */ public void setTextColor(int textColor) { mTextView.setTextColor(textColor); } /** * 设置字体大小 * * @param textSize */ public void setTextSize(int textSize) { mTextView.setTextSize(textSize); } /** * 设置图片 * * @param resId */ public void setImageResource(int resId) { mImageView.setImageResource(resId); } /** * 设置图片背景 */ public void setBackgroundResource(int resId) { mImageView.setBackgroundResource(resId); } /** * 设置图片的不透名度 * * @param alpha */ public void setImageAlpha(int alpha) { mImageView.setAlpha(alpha); } /** * 设置图片的大小 * 这里面需要使用LayoutParams这个布局参数来设置 * * @param width * @param height */ public void setImageSize(int width, int height) { LayoutParams layoutParams = new LayoutParams(width, height); mImageView.setLayoutParams(layoutParams); } /** * image点击事件的回调 * * @param listener */ public void setOnClickListener(OnItemClickListener listener) { lis = null; lis = listener; mImageView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { lis.onImageClick(); } }); } /** * 点击事件接口 * * @author linc */ private OnItemClickListener lis; public interface OnItemClickListener { public void onImageClick(); } }
LevelMenu.java
public class LevelMenu extends LinearLayout { private LevelMenuItem item1, item2, item3; private String TAG = this.getClass().getName(); public LevelMenu(Context context) { super(context); } public LevelMenu(Context context, AttributeSet attrs) { super(context, attrs); LayoutInflater layoutInflater = (LayoutInflater) context. getSystemService(Context.LAYOUT_INFLATER_SERVICE); layoutInflater.inflate(R.layout.level_menu, this); initWidget(); } public boolean setOnClickListener(int id, LevelMenuItem.OnItemClickListener lis) { boolean bRes = false; switch (id) { case R.id.item1: { item1.setOnClickListener(lis); bRes = true; } break; case R.id.item2: { item2.setOnClickListener(lis); bRes = true; } break; case R.id.item3: { item3.setOnClickListener(lis); bRes = true; } break; } return bRes; } private void initWidget() { item1 = (LevelMenuItem) findViewById(R.id.item1); item2 = (LevelMenuItem) findViewById(R.id.item2); item3 = (LevelMenuItem) findViewById(R.id.item3); } }layout使用 LevelMenu
<com.example.qinghua_liu.myapplication.LevelMenu android:layout_below="@+id/item0" android:id="@+id/levelMenu" android:layout_width="wrap_content" android:layout_height="wrap_content" />
自定义控件组的使用:
levelMenu = (LevelMenu) findViewById(R.id.levelMenu); levelMenu.setOnClickListener(R.id.item1, new LevelMenuItem.OnItemClickListener() { @Override public void onImageClick() { Intent it = new Intent(Main2Activity.this, ActivityMain3Activity.class); try { startActivity(it); } catch (Exception e) { Log.e(TAG, e.toString()); } Log.e(TAG, "item1 clicked"); } }); levelMenu.setOnClickListener(R.id.item2, new LevelMenuItem.OnItemClickListener() { @Override public void onImageClick() { Intent it = new Intent(Main2Activity.this, ActivityMain4Activity.class); try { startActivity(it); } catch (Exception e) { Log.e(TAG, e.toString()); } Log.e(TAG, "item2 clicked"); } }); levelMenu.setOnClickListener(R.id.item3, new LevelMenuItem.OnItemClickListener() { @Override public void onImageClick() { WorkClass workClass =new WorkClass(); workClass.test(); Log.e(TAG, "item3 clicked"); } });
Activity Layout: