@linux1s1s
2019-02-14T17:44:34.000000Z
字数 6461
阅读 2211
AndroidWidget
2015-05
了解Android组件开发之前需要先对Android View有个大体的了解,建议看一下博客: Android View 分析(上)、Android View 分析(中)Android View 分析(下),这三篇博客对View从头到尾分析了一遍,虽然没有涉及更深的层次,但是应付组件开发应该够了,这篇博客是组件开发的起步,我们先来说一下比较简单的组件开发。
我们知道在Android View 分析(下)中,如果调用invalidate
方法会引起重绘,也就是invalidate(...)
--> onDraw(...)
, 而如果想手动绘制控件,那么只需要在合适的时机调用invalidate(...)
方法引起重绘,然后在onDraw(...)
方法中绘制你定制的内容即可,看下面的Demo
public class CounterView extends View implements OnClickListener {
private Paint mPaint;
private Rect mBounds;
private int mCount;
public CounterView(Context context, AttributeSet attrs) {
super(context, attrs);
//直接在构造器中获取画笔
mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mBounds = new Rect();
setOnClickListener(this);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas); //重写onDraw方法
mPaint.setColor(Color.BLUE); //设置画笔颜色
canvas.drawRect(0, 0, getWidth(), getHeight(), mPaint); //画一个长方形
mPaint.setColor(Color.YELLOW); //重设画笔颜色
mPaint.setTextSize(30); //设置文本字体大小
String text = String.valueOf(mCount);
mPaint.getTextBounds(text, 0, text.length(), mBounds);
float textWidth = mBounds.width();
float textHeight = mBounds.height(); //获取字体高宽
canvas.drawText(text, getWidth() / 2 - textWidth / 2, getHeight() / 2 + textHeight / 2, mPaint); //画文本,注意这个文本的位置。
}
@Override
public void onClick(View v) {
mCount++;
invalidate(); //在合适的时机调用invalidate()引发重绘
}
}
接下来看看XML文件
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<com.example.customview.CounterView
android:layout_width="100dp"
android:layout_height="100dp"
android:layout_centerInParent="true" />
</RelativeLayout>
看一下运行结果:
需要每次点击一下View才能更新计数,当然这里可以自己写一个线程不停的更新计数。
这一种方式的自定义最没有技术含量,如果仅仅是将通用的View控件组合在一起的话,当然如果App中这样的组件出现的比较频繁,可以组合这样一种组件,以供App使用,同时也方便维护。
考虑到App中出现比较多这样的组件
那么可以将这个组件封装一下,暂且取个名字TitleView
,接下来让我们来完成这个TitileView
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="50dp"
android:background="#ffcb05" >
<Button
android:id="@+id/button_left"
android:layout_width="60dp"
android:layout_height="40dp"
android:layout_centerVertical="true"
android:layout_marginLeft="5dp"
android:background="@drawable/back_button"
android:text="Back"
android:textColor="#fff" />
<TextView
android:id="@+id/title_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:text="This is Title"
android:textColor="#fff"
android:textSize="20sp" />
</RelativeLayout>
public class TitleView extends FrameLayout {
private Button leftButton;
private TextView titleText;
public TitleView(Context context, AttributeSet attrs) {
super(context, attrs);
LayoutInflater.from(context).inflate(R.layout.title, this);
titleText = (TextView) findViewById(R.id.title_text);
leftButton = (Button) findViewById(R.id.button_left);
leftButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
((Activity) getContext()).finish();
}
});
}
public void setTitleText(String text) {
titleText.setText(text);
}
public void setLeftButtonText(String text) {
leftButton.setText(text);
}
public void setLeftButtonListener(OnClickListener l) {
leftButton.setOnClickListener(l);
}
}
下面我们来看看如何使用上面定义好的TitleView
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<com.example.customview.TitleView
android:id="@+id/title_view"
android:layout_width="match_parent"
android:layout_height="wrap_content" >
</com.example.customview.TitleView>
</RelativeLayout>
这里主要是对现有的控件进行扩展,比如现在有个需求,对现有的ListView进行扩展,增加单个Item滑动提供删除功能。
我们打算手动加载删除Button,当然也可以在Item的XML中直接添加这个Button,然后检测手势。然后决定是否显示还是隐藏删除Button。这个检测手势的操作如下
//首先生成一个GestureDetector的实例
gestureDetector = new GestureDetector(getContext(), this);
//然后判断X轴的偏移量是不是大于Y轴的偏移量,如果条件满足表示左右滑动,否则表示上下滑动
if (Math.abs(velocityX) > Math.abs(velocityY))
{
...
}
<?xml version="1.0" encoding="utf-8"?>
<Button xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/delete_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/delete_button" >
</Button>
public class MyListView extends ListView implements OnTouchListener,
OnGestureListener {
private GestureDetector gestureDetector;
private OnDeleteListener listener;
private View deleteButton;
private ViewGroup itemLayout;
private int selectedItem;
private boolean isDeleteShown;
public MyListView(Context context, AttributeSet attrs) {
super(context, attrs);
gestureDetector = new GestureDetector(getContext(), this);
setOnTouchListener(this);
}
public void setOnDeleteListener(OnDeleteListener l) {
listener = l;
}
@Override
public boolean onTouch(View v, MotionEvent event) {
if (isDeleteShown) {
itemLayout.removeView(deleteButton);
deleteButton = null;
isDeleteShown = false;
return false;
} else {
return gestureDetector.onTouchEvent(event);
}
}
@Override
public boolean onDown(MotionEvent e) {
if (!isDeleteShown) {
selectedItem = pointToPosition((int) e.getX(), (int) e.getY());
}
return false;
}
@Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,
float velocityY) {
if (!isDeleteShown && Math.abs(velocityX) > Math.abs(velocityY)) {
deleteButton = LayoutInflater.from(getContext()).inflate(
R.layout.delete_button, null);
deleteButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
itemLayout.removeView(deleteButton);
deleteButton = null;
isDeleteShown = false;
listener.onDelete(selectedItem);
}
});
itemLayout = (ViewGroup) getChildAt(selectedItem
- getFirstVisiblePosition());
RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(
LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
params.addRule(RelativeLayout.ALIGN_PARENT_RIGHT);
params.addRule(RelativeLayout.CENTER_VERTICAL);
itemLayout.addView(deleteButton, params);
isDeleteShown = true;
}
return false;
}
@Override
public boolean onSingleTapUp(MotionEvent e) {
return false;
}
@Override
public void onShowPress(MotionEvent e) {
}
@Override
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX,
float distanceY) {
return false;
}
@Override
public void onLongPress(MotionEvent e) {
}
public interface OnDeleteListener {
void onDelete(int index);
}
}
这里在MyListView的构造方法中创建了一个GestureDetector的实例用于监听手势,然后给MyListView注册了touch监听事件。然后在onTouch()方法中进行判断,如果删除按钮已经显示了,就将它移除掉,如果删除按钮没有显示,就使用GestureDetector来处理当前手势。
当手指按下时,会调用OnGestureListener的onDown()方法,在这里通过pointToPosition()方法来判断出当前选中的是ListView的哪一行。当手指快速滑动时,会调用onFling()方法,在这里会去加载delete_button.xml这个布局,然后将删除按钮添加到当前选中的那一行item上。注意,我们还给删除按钮添加了一个点击事件,当点击了删除按钮时就会回调onDeleteListener的onDelete()方法,在回调方法中应该去处理具体的删除操作。
最后看一下运行结果:
本文转载并做了适当修改:http://blog.csdn.net/guolin_blog/article/details/17357967