完美实现Android自定义控件---以自定义带图片和文本的Button为例

本文详细介绍了如何实现一个自定义的Android控件,该控件结合了图片和文本,类似于系统Button。步骤包括定义布局、创建对应类、在布局中使用以及代码设置。最终达到的效果是,点击控件任一区域都会触发onClick()方法,同时内部组件状态可变。文章还提供了参考链接和对CSDN编辑器的评价。
摘要由CSDN通过智能技术生成

像系统自带控件的一样,做一个真正的控件。

第一步:定义一个有图片和文本的布局:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 
    android:orientation="horizontal" 
    android:layout_width="wrap_content" 
    android:layout_height="wrap_content" 
    > 
<ImageView 
    android:id="@+id/widget_image_text_btn_Img" 
    android:layout_width="wrap_content" 
    android:layout_height="wrap_content" 
    /> 
<TextView 
    android:id="@+id/widget_image_text_btn_TV" 
    android:layout_width="wrap_content" 
    android:layout_height="wrap_content" 
    android:textColor="#000000" 
    android:layout_marginLeft="8dip" 
    android:layout_gravity="center_vertical" 
    /> 
</LinearLayout> 


第二步:定义一个与这个布局对应的类:

package com.widget;

import com.example.testdrawable.R;
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.res.ColorStateList;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.util.Log;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
import android.widget.Toast;

public class ImgTextBtnH extends LinearLayout {

	private static final String TAG = ImgTextBtnH.class.getSimpleName();
	
	private ImageView mImg; 
    private TextView  mTV; 
    private Button mBtn;
    private Context mContext = null;
 
    public ImgTextBtnH(Context context) { 
        this(context, null); 
        mContext = context;
    } 
 
    protected void inflaterLayout(Context context) {
        // 导入布局 
        LayoutInflater.from(context).inflate(R.layout.widget_image_text_btn_horizontal_layout, this, true); 
    }
    
    
    @SuppressLint("NewApi")
	public ImgTextBtnH(Context context, AttributeSet attrs) { 
        super(context, attrs); 
        mContext = context;
        
        Resources.Theme theme = context.getTheme();
        
        inflaterLayout(context);
        // 导入布局 
//        LayoutInflater.from(context).inflate(R.layout.widget_image_text_btn_layout, this, true); 
        mImg = (ImageView) findViewById(R.id.widget_image_text_btn_Img); 
        mTV = (TextView) findViewById(R.id.widget_image_text_btn_TV); 
        mBtn = (Button) findViewById(R.id.widget_image_text_btn_Btn);
        
        //不能够使用ImageView的click方法, 在android里点击事件消息是从内向外传递的,设置了false之后才能传递出来给LinearLayout
        mImg.setClickable(false); //谢谢 Brightshadow11111 朋友的提醒
        
        TypedArray typeArray = context.obtainStyledAttributes(attrs,
                R.styleable.ImgTextBtn);
        if (typeArray == null) {
        	return;
        }
        
        int count = typeArray.getIndexCount();
        int resId = 0;
        for (int i = 0; i < count; i++) {
        	int attr = typeArray.getIndex(i);
        	switch(attr) {
        	case R.styleable.ImgTextBtn_ImgDraw:
        		Drawable background = typeArray.getDrawable(attr);
        		int sdk = android.os.Build.VERSION.SDK_INT;
        		if(sdk < android.os.Build.VERSION_CODES.JELLY_BEAN) {
        		    mImg.setBackgroundDrawable(background);
        		} else {
        			mImg.setBackground(background);
        		}
        		break;
        	case R.styleable.ImgTextBtn_ImgDrawWidth:
        		int imageWidth = typeArray.getDimensionPixelSize(attr, -1);
        		mImg.setMaxWidth(imageWidth);
        		mImg.setMinimumWidth(imageWidth);
        		break;
        	case R.styleable.ImgTextBtn_ImgDrawHeight:
        		int imageHeight = typeArray.getDimensionPixelSize(attr, -1);
        		mImg.setMaxHeight(imageHeight);
        		mImg.setMinimumHeight(imageHeight);
        		break;
        	case R.styleable.ImgTextBtn_ImgDrawMinWidth:
        		int imageMinWidth = typeArray.getDimensionPixelSize(attr, -1);
        		mImg.setMinimumHeight(imageMinWidth);
        		break;
        	case R.styleable.ImgTextBtn_ImgDrawMinHeight:
        		int imageMinHeight = typeArray.getDimensionPixelSize(attr, -1);
        		mImg.setMinimumHeight(imageMinHeight);
        		break;
        	case R.styleable.ImgTextBtn_ImgDrawMaxHeight:
        		int imageMaxHeight = typeArray.getDimensionPixelSize(attr, -1);
        		mImg.setMaxHeight(imageMaxHeight);
        		break;
        	case R.styleable.ImgTextBtn_ImgDrawMaxWidth:
        		int imageMaxWidth = typeArray.getDimensionPixelSize(attr, -1);
        		mImg.setMaxWidth(imageMaxWidth);
        		break;
        	case R.styleable.ImgTextBtn_TVText:
        		mTV.setText(typeArray.getText(attr));
        		break;
        	case R.styleable.ImgTextBtn_TVTextSize:
        		mTV.setTextSize(typeArray.getDimensionPixelSize(attr, 15));
        		break;
        	}
        }
        typeArray.recycle();
    } 
    
    @Override
    public void refreshDrawableState() {
    	// TODO Auto-generated method stub
    	super.refreshDrawableState();
        //------------接下来处理联动效果,关键代码,请认真看-------------------
        //------------ ImageView控件的联动刷新 -------------------
    	Drawable imgDrawable = mImg.getBackground(); //获取drawable资源
    	Log.d(TAG, "drawable = " + imgDrawable);
    	if (imgDrawable != null && imgDrawable.isStateful()) {
            //关键中的关键,根据当前状态设置drawable的状态,本来应该是LinearLayout的getDrawableState()状态,
            //但是现在是实现联动效果,而且获取的ImageView的getDrawState()结果不对。
    		imgDrawable.setState(this.getDrawableState()); 
    	}
    	
    	//------------- TextView的联动刷新, 抱歉, 无法刷新 ------------------
        //这块代码很快写出来了,但是写出来后发现,颜色总是停留在最后一次的颜色上,后面再点击都无法改变颜色了,
        //才恍然大悟,mTV.setTextColor()是设置TextView内部的ColorStateList对象的,这样会清掉原先的
        //res/color/colorSelector.xml里的设置,导致只有一种颜色值。应该赋值给TextView内部的private int mCurTextColor;
        //可惜没有并设置mCurTextColor值的接口,不知到使用反射能否做到,有兴趣朋友可以一试。
        //既然Text的color是靠TextPaint刷出来的,那么是否可以改变TextPaint的颜色值呢?发现有接口getPaint可以获取到TextPaint,
        //但是设置了color值之后,还是不行,哦,原来在onDraw()函数中,重新设置了颜色值,所以白设了。
        //总而言之,联动刷新颜色是不行滴,只能设一个颜色(对于我目前来讲功能已经是足够了) 哈哈,讲的够详细吧~~
    	ColorStateList mTextColor = mTV.getTextColors();
    	
    	int color = mTextColor.getColorForState(this.getDrawableState(), 0);
        if (mTV.getCurrentTextColor() != color) {
        	mTV.getPaint().setColor(color);
//        	mTV.setTextColor(color);
        	mTV.invalidate();
        }
        
        //-----------如果有个Button的话就可以在这里设置改变,效果同上,略---------------
//        Drawable btnDrawable = mBtn.getBackground();
//    	Log.d(TAG, "drawable = " + btnDrawable);
//    	if (btnDrawable != null && btnDrawable.isStateful()) {
//    		btnDrawable.setState(this.getDrawableState());
//    	}
//    	
//    	ColorStateList mTextColor2 = mBtn.getTextColors();
//    	int color2 = mTextColor2.getColorForState(this.getDrawableState(), 0);
//        if (mBtn.getCurrentTextColor() != color2) {
//        	mBtn.setTextColor(color2);
//        }
 
    }

    /**
     * 设置图片资源
     */ 
    public void setImgViewResource(int resId) { 
        mImg.setImageResource(resId); 
    } 
    
    /**
     * 设置图片
     */ 
    public void setImgViewResource(Bitmap bitmap) { 
        mImg.setImageBitmap(bitmap); 
    } 
 
    /**
     * 设置显示的文字
     */ 
    public void setTextViewText(int resId) { 
        mTV.setText(resId); 
    } 
    /**
     * 设置显示的文字
     */ 
    public void setTextViewText(String text) { 
        mTV.setText(text); 
    } 
 
}

第三步:

在value/下面定义一个attrs.xml文件,内容如下:
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <declare-styleable name="ImgTextBtn">
        <attr name="ImgDraw" format="reference"></attr>
        <attr name="ImgDrawWidth" format="dimension"></attr>
        <attr name="ImgDrawHeight" format="dimension"></attr>
        <attr name="ImgDrawMinWidth" format="dimension"></attr>
        <attr name="ImgDrawMinHeight" format="dimension"></attr>
        <attr name="ImgDrawMaxWidth" format="dimension"></attr>
        <attr name="ImgDrawMaxHeight" format="dimension"></attr>
        
        <attr name="TVText" format="reference|string"></attr>
        <attr name="TVTextSize" format="dimension"></attr>
        <attr name="TVTextColor" format="reference|color"></attr>
    </declare-styleable>
    
</resources>

第四步:

直接在布局中使用

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:CustomAttrs="http://schemas.android.com/apk/res/com.example.testdrawable"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <com.widget.ImgTextBtn
        android:id="@+id/imageTextButton1"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical"
        android:layout_above="@+id/textView1"
        android:layout_centerHorizontal="true"
        android:layout_marginBottom="35dp"
        android:background="@drawable/preference_round_rect_light_blue"
        CustomAttrs:TVText="@string/hello_world"
        CustomAttrs:TVTextSize="@dimen/dimens_Padding_normal"
        CustomAttrs:ImgDraw="@drawable/test_btn_selector">
    </com.widget.ImgTextBtn>

</RelativeLayout>


第五步:

代码中设置:

ImgTextBtn mBtn = (ImgTextBtn) findViewById(R.id.imageTextButton1);
		mbtn.setOnClickListener(new View.OnClickListener() {
			
			@Override
			public void onClick(View v) {
				// TODO Auto-generated method stub
				Toast.makeText(MainActivity.this, "OK OK OK", Toast.LENGTH_LONG).show();
			}
		});


运行效果如下:



评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值