android 自定义View(二) 自定义属性和带滚动的View

自定义View的属性

  在上一章中讲了那么多,这一章开始就进行实战了。首先来一发自定义View属性的demo。
自定义View属性的步骤分为以下3步。
(1) 新建一个attrs.xml文件,在这个资源文件中定义我们需要的属性。
(2)新建一个自定义的View,对其中的的方法进行重写。
(3)在layout中引用这个新的控件。
先进行第一步。在values文件下新建一个attrs.xml文件。

<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <attr name="testText" format="string"/>
    <attr name="testColor" format="color"/>
    <attr name="testSize" format="dimension"/>
    <attr name="testBackGroudColor" format="color"/>
     <declare-styleable name="CustomTitleView">  
        <attr name="testText" />
        <attr name="testColor" />
        <attr name="testSize" />
        <attr name="testBackGroudColor"/>
    </declare-styleable>
</resources>

这里我们定义了一个属性组,包含了文字,文字颜色,文字大小以及控件背景色,且规定了它们的类型分别为string ,color,dimension。
  然后在自定义的View中引用这些属性。

public class TestView extends View{

    private String testText;//文本
    private int testColor;//文本颜色
    private int testSize;//文字大小
    private int testBackGroudColor;//背景色
    private Rect mBound;
    private Paint mPaint;

    public TestView(Context context) {
        this(context, null);
    }

    public TestView(Context context,AttributeSet attrs) {
        this(context, attrs, 0);
    }
    public TestView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        // TODO Auto-generated constructor stub
        //构造函数中引用属性
        TypedArray ta = context.getTheme().obtainStyledAttributes(attrs,R.styleable.CustomTitleView, defStyleAttr, 0);
        int n = ta.getIndexCount();
        for (int i = 0; i < n; i++)
        {  
            int attr = ta.getIndex(i);
            switch (attr)  
            {  
            case R.styleable.CustomTitleView_testText:  
                testText = ta.getString(attr);  
                break;  
            case R.styleable.CustomTitleView_testColor:  

                testColor = ta.getColor(attr, Color.GRAY);
                break;  
            case R.styleable.CustomTitleView_testSize:

                testSize = ta.getDimensionPixelSize(attr, (int) TypedValue.applyDimension(
                        TypedValue.COMPLEX_UNIT_SP, 16, getResources().getDisplayMetrics()));
                break;  
            case R.styleable.CustomTitleView_testBackGroudColor:
                testBackGroudColor = ta.getColor(attr, Color.BLACK);
            }  

        }  
        ta.recycle();
        mPaint = new Paint();
        mPaint.setTextSize(testSize);
        mBound = new Rect();
        mPaint.getTextBounds(testText, 0, testText.length(), mBound);  
    }

    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)  
    {  
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);  
    }

    protected void onDraw(Canvas canvas)  
    {  
        mPaint.setColor(testBackGroudColor);  
        canvas.drawRect(0, 0, getMeasuredWidth(), getMeasuredHeight(), mPaint);  

        mPaint.setColor(testColor);  
        canvas.drawText(testText, getWidth() / 2 - mBound.width() / 2, getHeight() / 2 + mBound.height() / 2, mPaint);  
    }

接下来进行最后一步,在布局中引用自定义的View

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:custom="http://schemas.android.com/apk/res/com.example.myview"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context="com.example.myview.MainActivity" >
   <com.example.myview.TestView 
        android:layout_width="200dp"
        android:layout_height="100dp"
        custom:testText="12345"
        custom:testColor="#ff0000"
        custom:testSize="40sp"
        custom:testBackGroudColor="#00ff00"/>
</RelativeLayout>

注意命名控件的引用xmlns:custom=”http://schemas.android.com/apk/res/com.example.myview”。
具体代码将传到github上,效果可自行下载后查看。

自定可以随意拖动的滚动View

这个Demo中包含了View的滚动和点击事件。涉及到了View自定义中的onTouchEvent onMeasure等。
先初始化一个文本

    protected void init() {
        mPaint = new Paint();
        mPaint.setColor(Color.GREEN);
        mPaint.setTextSize(80);
        Rect rect = new Rect();
        mPaint.getTextBounds(text[0], 0, text[0].length(), rect);
        textWidth = rect.width();
        textHeight = rect.height();
        x = getLeft() + getPaddingLeft();
        startY = getTop() + textHeight + getPaddingTop() - 7;// 文字基线 需要调整
        Log.d("hk", "textHeight startY  = "+textHeight+" "+startY);
        endY = startY + textHeight + getPaddingBottom();
        Log.d("hk", "endY  getPaddingBottom() "+endY+" "+getPaddingBottom());
        nextStartY = getTop() - 7;
        firstY = startY;
        secondY = nextStartY;
    }

在对这个文本的大小进行测量

    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        int width = measureWidth(widthMeasureSpec);
        int height = measureHeight(heightMeasureSpec);
        setMeasuredDimension(width, height);
    }
    private int measureHeight(int heightMeasureSpec) {
        int result = 0;
        int size = MeasureSpec.getSize(heightMeasureSpec);
        int mode = MeasureSpec.getMode(heightMeasureSpec);
        if (mode == MeasureSpec.EXACTLY) {
            result = size;
        } else {
            result = (int) (getPaddingTop() + getPaddingBottom() + textHeight);
            if (mode == MeasureSpec.AT_MOST) {
                result = Math.min(result, size);
            }
        }
        return result;
    }

    private int measureWidth(int widthMeasureSpec) {
        int result = 0;
        int size = MeasureSpec.getSize(widthMeasureSpec);
        int mode = MeasureSpec.getMode(widthMeasureSpec);
        if (mode == MeasureSpec.EXACTLY) {
            result = size;
        } else {
            result = (int) (getPaddingLeft() + getPaddingRight() + textWidth);
            if (mode == MeasureSpec.AT_MOST) {
                result = Math.min(result, size);
            }
        }
        return result;
    }

最后在onDraw中让文字进行滚动。
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
Log.d(“HK”, “onDraw”);
//启动滚动
if (!isScrooll) {
mHandler.sendEmptyMessageDelayed(CHANGE_SPEECH, 2000);
isScrooll = true;
}

    canvas.drawText(text[0], x, startY, mPaint);
    canvas.drawText(text[1], x, nextStartY, mPaint);
    startY += speech;
    nextStartY += speech;

    //超出View的控件时
    if (startY > endY || nextStartY > endY) {
        if (startY > endY) {
            //第一次滚动过后交换值
            startY = secondY;
            nextStartY = firstY;
        } else if (nextStartY > endY) {
            //第二次滚动过后交换值
            startY = firstY;
            nextStartY = secondY;
        }
        speech = 0;
        isScrooll = false;
    }
    invalidate();//刷新View,View的刷星又会调用到draw ,因此可以循环下去
}

为了使该控件能够接受拖动事件,需要重写onTouchEvent。

    public boolean onTouchEvent(MotionEvent event) {

        float x;
        float y;
        x = event.getRawX();
        y = event.getRawY();
        switch(event.getAction()) {
        case MotionEvent.ACTION_DOWN:

            Log.d("HK", "ACTION_DOWN"+x+" "+y);
            break;
        case MotionEvent.ACTION_MOVE:
            int lefttopx = (int) (x-textWidth/2);
            int lefttopy = (int) (y-textHeight/2-180);
            int rightbottomx = (int) (lefttopx+textWidth);
            int rightbottomy = (int) (lefttopy+textHeight);
            //if(Math.abs(rightbottomx-lefttopx)>50&&Math.abs(rightbottomy-lefttopy)>20)
            layout(lefttopx, lefttopy, rightbottomx, rightbottomy);//重新布局
            break;
        case MotionEvent.ACTION_UP:
            Log.d("HK", "ACTION_UP");
            break;
        }
        return true;
    }

这样就可以实现一个可拖动的,切能够自己滚动显示的View。具体代码见github。
GITHUB:
https://github.com/everyhappy/myViews

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值