Android实战技巧之组合控件
fmms
13年前
组合控件是自定义控件的一种,只不过它是由其他几个原生控件组合而成,故名组合控件。
在实际项目中,GUI会遇到一些可以提取出来做成自定义控件情况。
一个自定义控件的好处就是把一些需要模块化的UI和逻辑放在一起,做到了高内聚,向其他模块提供接口并很少
依赖外界,这样就是低耦合。一个自定义控件就是一个封闭的王国,这里由你掌控。
上述是我自己的一个体会,想必大家也会常做自定义控件吧,就像逻辑部分的模块化一样。
下面我要做一个例子,请看完成图。
下面一排图片加文字就是组合控件了,我是怎么做的呢?
其实这里用到了两个组合控件,一个是图片+文字,我把它叫一个Item,而三个在一起就是另一个控件了。
重点看这个Item,它有自己的属性如图片、文字、图片大小、文字大小、不透明度等等。这些把它定义在attr文件中,然后在xml文件中
配置,就像我们用原生控件一样。
先看attr文件。
<?xml version="1.0" encoding="utf-8"?> <resources> <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> </resources>这个文件在values下,和string文件同级。把你自己要定义的属性都写在这里吧。format是属性的“单位”,如果你要问有多少中format呀?答案在这里。
有了属性了,下面看看布局文件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:orientation="vertical" > <ImageView 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" /> </LinearLayout>这里唯一值得一说的是文本的颜色。大家看见他是8位的,前两位是表示不透明度的,后六位是表示颜色的,三色,范围都是00~ff。如果在java中设置颜色,需要这样。
setTextColor(0x23ffffff);关于不透明度,一般美工会定义。有些要求不透明如30%这样的,可以用整型换算一下。00~ff对应十进制为0~255,那么30%就是255x0.3=76.5,用科学计算机换算为4c。
然后我们就要写一个类,我这继承子线性布局。有两个构造函数,我们主要在两个参数的函数中工作。
public class LevelMenuItem extends LinearLayout { public LevelMenuItem(Context context, AttributeSet attrs) { super(context, attrs); }这个类中我们要完成的工作是,初始化控件属性、提供外部修改属性的接口、控件点击的回调接口。
此类完整代码:
package com.linc.game; import android.content.Context; import android.content.res.TypedArray; import android.util.AttributeSet; import android.view.LayoutInflater; import android.view.View; import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.TextView; /** * 自定义一个关卡 * 共有7个属性,看attr文件 * 在程序中提供修改这7个属性的接口, * 一个自定义控件的任务就算完成。 * 一个自定义控件的好处就是把一些需要模块化的 * UI和逻辑放在一起,做到了高内聚,向其他模块提供接口并很少 * 依赖外界,这样就是低耦合。一个自定义控件就是一个封闭的王国, * 这里由你掌控。 * * 编写时,如果遇到在attr里写好属性,但是在这里认不出来, * 就clean一下项目。切记。 * * @author linc * */ public class LevelMenuItem extends LinearLayout { private TextView mTextView = null; private ImageView mImageView = null; private OnItemClickListener mOnClickListener = 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) { this.mOnClickListener = listener; mImageView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { mOnClickListener.onImageClick(); } }); } /** * 点击事件接口 * @author linc * */ public interface OnItemClickListener { public void onImageClick(); } }好,一个完整的组合控件就做好了,那么,我们如何使用呢?
我要在LevelMenu中用它。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/com.linc.game" android:layout_width="fill_parent" android:layout_height="fill_parent" android:orientation="horizontal"> <com.linc.game.LevelMenuItem android:id="@+id/item1" android:layout_width="70dp" android:layout_height="80dp" linc:text="@string/item1" linc:text_size="14sp" linc:text_color="#80fa8072" linc:image_src="@drawable/orange_button_selector" linc:image_alpha="128" linc:image_height="48dp" linc:image_width="48dp" /> <com.linc.game.LevelMenuItem android:id="@+id/item2" android:layout_marginLeft="20dp" android:layout_width="70dp" android:layout_height="80dp" linc:text="@string/item2" linc:text_size="14sp" linc:text_color="#ffeee8aa" linc:image_src="@drawable/red_button_selector" linc:image_alpha="255" linc:image_height="48dp" linc:image_width="48dp" /> <com.linc.game.LevelMenuItem android:id="@+id/item3" android:layout_marginLeft="20dp" android:layout_width="70dp" android:layout_height="80dp" linc:text="@string/item3" linc:text_size="14sp" linc:text_color="#80cd853f" linc:image_src="@drawable/yellow_button_selector" linc:image_alpha="128" linc:image_height="48dp" linc:image_width="48dp" /> </LinearLayout>加入自己包名的索引剩下的就一目了然了。
LevelMenu.java
package com.linc.game; import com.linc.game.LevelMenuItem.OnItemClickListener; import android.content.Context; import android.util.AttributeSet; import android.util.Log; import android.view.LayoutInflater; import android.widget.LinearLayout; public class LevelMenu extends LinearLayout { private LevelMenuItem item1,item2,item3; 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(); } private void initWidget() { item1 = (LevelMenuItem)findViewById(R.id.item1); item2 = (LevelMenuItem)findViewById(R.id.item2); item3 = (LevelMenuItem)findViewById(R.id.item3); item1.setOnClickListener(new OnItemClickListener() { @Override public void onImageClick() { Log.e("dfjdkfjd","dfdfd"); } }); } }在处理图片点击事件的时候,我用到了选择器(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>好,组合控件的例子先到这里,实际功能在下一个实战技巧中演练。
大家在做自定义控件时需要注意的是:
1、自定义控件类不能是是抽象类
2、要用(Context context, AttributeSet attrs)这个构造函数
否则报错:android.view.InflateException: Binary XML file line #15: Error inflating cla。。。