Android编程自定义组件实例详解
什么是自定义组件
自定义组件是指在 Android 中自己定义一个组件(View),并通过布局文件或代码使用这个组件,它不同于系统提供的常用组件,例如Button、TextView等。自定义组件可以根据需求自由定义功能和样式,扩展系统组件无法完成的功能。
自定义View的步骤
自定义View的基本步骤如下:
- 继承系统提供的View或View的子类,例如绘图可以继承View,自定义组件可以继承ViewGroup。
- 重写View的构造方法或其他方法,例如onDraw()方法。
- 在xml布局文件或该自定义View所属的Activity中使用这个自定义View。
自定义View的实现
以下是自定义View的一个简单实现,它是一个继承View的类,功能为显示一段文本。
public class MyTextView extends View {
private String mText;
private Paint mPaint;
public MyTextView(Context context) {
super(context);
init();
}
public MyTextView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.MyTextView);
mText = ta.getString(R.styleable.MyTextView_text);
ta.recycle();
}
private void init() {
mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mPaint.setTextSize(50);
mPaint.setColor(Color.BLACK);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (mText != null) {
canvas.drawText(mText, 0, mText.length(), 0, 50, mPaint);
}
}
public void setText(String text) {
mText = text;
invalidate();
requestLayout();
}
}
上述代码中,我们重写了View的构造方法,并对自定义属性进行解析,最终通过onDraw()方法绘制出我们自定义的View,同时提供一个setText()方法,可以通过代码设置View的内容。
在布局文件中使用MyTextView:
<com.example.customview.MyTextView
android:id="@+id/myTextView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello 自定义View"
/>
通过这样的方式使用自定义View,可以非常灵活地定制UI。
自定义组件的实现
自定义组件是指继承系统提供的ViewGroup或其子类,并在其中添加自己的View。我们来看一个简单的例子,这是一个带标题和内容的自定义ViewGroup。
public class MyViewGroup extends ViewGroup {
private TextView mTitleView;
private TextView mContentView;
public MyViewGroup(Context context) {
super(context);
init();
}
public MyViewGroup(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
private void init() {
mTitleView = new TextView(getContext());
mContentView = new TextView(getContext());
mTitleView.setTextSize(20);
mContentView.setTextSize(16);
addView(mTitleView);
addView(mContentView);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
int titleWidthSize = widthSize - getPaddingLeft() - getPaddingRight();
int titleHeightSize = (int) mTitleView.getPaint().measureText("A") + getPaddingTop() + getPaddingBottom();
mTitleView.measure(MeasureSpec.makeMeasureSpec(titleWidthSize, MeasureSpec.EXACTLY),
MeasureSpec.makeMeasureSpec(titleHeightSize, MeasureSpec.EXACTLY));
int contentWidthSize = widthSize - getPaddingLeft() - getPaddingRight();
int contentHeightSize = heightSize - titleHeightSize - getPaddingTop() - getPaddingBottom();
mContentView.measure(MeasureSpec.makeMeasureSpec(contentWidthSize, MeasureSpec.EXACTLY),
MeasureSpec.makeMeasureSpec(contentHeightSize, MeasureSpec.EXACTLY));
setMeasuredDimension(widthSize, titleHeightSize + contentHeightSize + getPaddingTop() + getPaddingBottom());
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
int titleLeft = getPaddingLeft();
int titleTop = getPaddingTop();
int titleRight = titleLeft + mTitleView.getMeasuredWidth();
int titleBottom = titleTop + mTitleView.getMeasuredHeight();
mTitleView.layout(titleLeft, titleTop, titleRight, titleBottom);
int contentLeft = getPaddingLeft();
int contentTop = titleTop + mTitleView.getMeasuredHeight();
int contentRight = contentLeft + mContentView.getMeasuredWidth();
int contentBottom = contentTop + mContentView.getMeasuredHeight();
mContentView.layout(contentLeft, contentTop, contentRight, contentBottom);
}
public void setTitle(String title) {
mTitleView.setText(title);
}
public void setContent(String content) {
mContentView.setText(content);
}
}
在布局文件中使用MyViewGroup:
<com.example.customview.MyViewGroup
android:id="@+id/myViewGroup"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="10dp">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="This is title"
android:textStyle="bold"/>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="This is content"/>
</com.example.customview.MyViewGroup>
上述代码中我们继承了ViewGroup,添加了两个子View,重写了onMeasure()方法和onLayout()方法来确定子View的大小和位置,同时提供了两个方法来动态设置标题和内容。
示例说明
下面提供两个示例来说明自定义组件的使用。
示例1:自定义带发送按钮的EditText
假设我们需要一个带发送按钮的EditText,这时候可以通过自定义ViewGroup实现。
public class SendEditText extends RelativeLayout {
private EditText mEditText;
private Button mSendButton;
public SendEditText(Context context) {
super(context);
init();
}
public SendEditText(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
private void init() {
LayoutInflater.from(getContext()).inflate(R.layout.view_send_edit_text, this, true);
mEditText = findViewById(R.id.edit_text);
mSendButton = findViewById(R.id.send_button);
mSendButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
// 处理发送逻辑
}
});
}
}
view_send_edit_text.xml:
<merge xmlns:android="http://schemas.android.com/apk/res/android">
<EditText
android:id="@+id/edit_text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"/>
<Button
android:id="@+id/send_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:text="发送"/>
</merge>
在布局文件中使用:
<com.example.customview.SendEditText
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
示例2:自定义带打分功能的RatingBar
假设我们需要一个带打分功能的RatingBar,这时候可以通过继承系统提供的RatingBar实现。
public class ScoreRatingBar extends RatingBar {
private OnRatingChangeListener mOnRatingChangeListener;
public ScoreRatingBar(Context context) {
super(context);
init();
}
public ScoreRatingBar(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
private void init() {
setOnRatingBarChangeListener(new OnRatingBarChangeListener() {
@Override
public void onRatingChanged(RatingBar ratingBar, float rating, boolean fromUser) {
if (mOnRatingChangeListener != null) {
mOnRatingChangeListener.onRatingChange((int) rating, fromUser);
}
}
});
}
public void setOnRatingChangeListener(OnRatingChangeListener listener) {
mOnRatingChangeListener = listener;
}
public interface OnRatingChangeListener {
void onRatingChange(int rating, boolean fromUser);
}
}
在布局文件中使用:
<com.example.customview.ScoreRatingBar
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:numStars="5"
android:stepSize="1"
android:rating="0"/>
通过setOnRatingChangeListener()方法可以监听打分变化的事件。
总结
Android自定义组件可以非常灵活地定制UI,可以根据需求自由定义功能和样式,扩展系统组件无法完成的功能。自定义View和自定义ViewGroup的实现步骤是基本一致的,而我们使用它们的方式也相似,它们都可以通过布局文件或代码使用。在使用自定义组件时,应该首先考虑系统提供的组件是否可以满足需求,只有当系统组件无法满足要求时再考虑自定义组件。
本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:Android编程自定义组件实例详解 - Python技术站