View第一部分自定义View 简单介绍及使用

Posted xzj_2013

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了View第一部分自定义View 简单介绍及使用相关的知识,希望对你有一定的参考价值。

自定义View的分类和使用场景分析

1.继承View/SurfaceView重写onDraw方法
  这种方法主要用于实现一些不规则的效果,即这种效果不方便通过布局的组合方式来达到,往往需要静态或者动态的显示一些不规则的图形。很显然  
  这需要通过绘制的方式来实现,即重写onDraw方法。采用这种方式需要自己支持wrap_content,并且padding也需要自己处理。
2.继承ViewGroup派生特殊的Layout
  这种方法主要用于实现自定义的布局,即除了LinearLayout,RelativeLayout,FrameLayout这几种系统的布局之外,我们需要重新定义一种新布局,  
  当某种效果看起来很像几种View组合在一起的时候,可以采用这种方式实现。采用这种方式稍微复杂一些,需要合适的处理ViewGroup的  
  测量布局这两个过程,并同时处理子元素的测量和布局过程。
3.继承特定的View(如TextView,ImageView等)
  这种方法比较常见,一般用于扩展某种已有的View的功能,比如TextView,这种方法的实现比较简单,不需要自己支持wrap_content和padding等。
4.继承特定的ViewGroup(如LinearLayout,FrameLayout)
  这种方法也比较常见,当某种效果看起来很像几种View组合起来的时候可以采用这种方法来实现,采用这种方法不需要自己处理ViewGroup的  
  测量和布局这两个过程。  
  需要注意这种方法和方法2的区别,一般来说方法2能实现的效果方法4也能实现,两者的主要区别在于方法2更加接近View的底层。  
  以上就是自定义View的四种方法,自定义View讲究的是灵活性,一种效果可能多种方式可以实现,我们需要做的是找到一种代价小最高效的方法去实现。

自定义View过程中需要注意的问题

要准确的实现自定义View,我们需要处理一些为题,这些问题可能会影响Viwq的正常使用,有些可能会导致内存泄露等问题。  

1.让View支持wrap_content
  继承的View或者ViewGroup如果不在onMeasure对wrap_content进行特殊处理,那么当外界使用wrap_content时,会无法达到预期的效果  
2.如果有必要,让你的View支持padding
  继承View的控件,如果不在onDraw中处理padding,那么padding属性将无法起作用,同时继承ViewGroup的控件也需要在  
  onMeasure和onLayout中考虑padding和magin对其效果的影响  
  
3.尽量不要在View中使用Handler,没必要
  View内部本身已经提供了Post系列的办法,完全可以替代handler的作用,除非你很明确需要使用Handler来发送消息  
  
4.View中如果有线程或者动画,需要及时停止,参考View#onDetachedFromWindow  
  如果有线程或者动画需要停止,那么onDetachedFromWindow是一个很好的时机,当包含此View的Activity退出或者当前View被remove时,View的  
  onDetachedFromWindow方法会被调用,和此方法对应的是onAttachedToWindow,当包含此View的Activity启动是View的onAttachedToWindow方法将会调用。  
  同时当View不可见时我们也需要停止线程和动画,如果不及时处理这种问题,很有可能会造成内存泄露。
5.View带有滑动嵌套情形时,需要处理好滑动冲突

自定义View示例

1.继承View

首先建立一个属性文件,声明了一个自定义属性集合,在这个集合中你可以添加许多自己需要的属性,其格式类型是name = 属性名 format = 属性值的类型

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <declare-styleable name = "circleview">
        <attr name = "circle_color" format = "color"/>
    </declare-styleable>
</resources>

然后是在View的构造方法中解析自定义的属性值并做处理

public CircleView(Context context, AttributeSet attrs, int defStyle) 
        super(context, attrs, defStyle);
        // TODO Auto-generated constructor stub
        //加载自定义属性集合circleview
        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.circleview);
        //从集合中获取设置的属性值
        mColor = a.getColor(R.styleable.circleview_circle_color, Color.RED);
        //解析完毕释放资源
        a.recycle();
        init();
    

最后在布局文件中使用自定义属性
需要注意的是一定要通过命名空间去使用自定义属性,即一定要先声明才能使用该属性

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    xmlns:mview="http://schemas.android.com/apk/res/res-auto"
    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=".MainActivity" >

    <com.example.myroundviewactivity.CircleView 
        android:id="@+id/m_view"
        android:layout_width="wrap_content"
        android:layout_height="match_parent"
        mview:circle_color = "#ffffff"
        />
</RelativeLayout>

一个简单的自定义View代码示例:

package com.example.myroundviewactivity;

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.view.View;

public class CircleView extends View 
    
    private int mColor = Color.RED;
    
    //抗锯齿
    private Paint mPaint  = new Paint(Paint.ANTI_ALIAS_FLAG); 

    public CircleView(Context context) 
        super(context);
        // TODO Auto-generated constructor stub
        init();
    

    public CircleView(Context context, AttributeSet attrs, int defStyle) 
        super(context, attrs, defStyle);
        // TODO Auto-generated constructor stub
        //自定义属性
        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.circleview);
        mColor = a.getColor(R.styleable.circleview_circle_color, Color.RED);
        a.recycle();
        init();
    

    public CircleView(Context context, AttributeSet attrs) 
       this(context,attrs,0);
    
    
    private void init()
        mPaint.setColor(mColor);
    
    
    
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) 
        // TODO Auto-generated method stub
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        //给View添加wrap_content支持
        int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec);
        int widthSpecSize = MeasureSpec.getSize(widthMeasureSpec);
        int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec);
        int heightSpecSize = MeasureSpec.getSize(heightMeasureSpec);
        if (widthSpecMode == MeasureSpec.AT_MOST && heightSpecMode == MeasureSpec.AT_MOST) 
            setMeasuredDimension(200, 200);
        else if(widthSpecMode == MeasureSpec.AT_MOST)
            setMeasuredDimension(200, heightSpecSize);
        else if(heightSpecMode == MeasureSpec.AT_MOST)
            setMeasuredDimension(widthSpecSize, 200);
        
    
    
    @Override
    protected void onDraw(Canvas canvas) 
        // TODO Auto-generated method stub
        super.onDraw(canvas);
        //使自定义View支持padding属性
        final int paddingleft = getPaddingLeft();
        final int paddingright = getPaddingRight();
        final int paddingtop = getPaddingTop();
        final int paddingbottom = getPaddingBottom();
        int width = getWidth()-paddingleft-paddingright;
        int height = getHeight()-paddingtop-paddingbottom;
        int radius = Math.max(width, height)/2;
        canvas.drawCircle(paddingleft+width/2, paddingtop+height/2, radius, mPaint);
    

2.继承ViewGroup派生特殊的Layout

http://blog.csdn.net/lmj623565791/article/details/38339817/

3.继承特定的View

package  com.example.commonview;

import android.content.Context;
import android.util.AttributeSet;


/**
 * 一直处于滚动状态的textview
 */
public class AlwaysMarqueeText extends TextView 
    
    public AlwaysMarqueeText(Context context) 
        super(context);
    
    
    public AlwaysMarqueeText(Context context, AttributeSet attrs) 
        super(context, attrs);
    
    
    public AlwaysMarqueeText(Context context, AttributeSet attrs, int defStyle) 
        super(context, attrs, defStyle);
    
    
    @Override
    public boolean isFocused() 
        return true;
    

4.继承特定的ViewGroup

package com.example.commonview;

import java.util.Date;

import android.annotation.SuppressLint;
import android.content.Context;
import android.os.Handler;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.LinearLayout;
import android.widget.TextView;



@SuppressLint("NewApi")
public class TimeAndDateView extends LinearLayout 
    
    private View rootView;
    
    private TextView tvDate;
    
    private TextView tvTime;
    
    private String currentTime;
    
    private String currentDate;
    
    private String currentWeek;
    
    private final static int UPDATE_TIME = 10001;
    
    private Handler mHandler = new Handler() 
        @Override
        public void handleMessage(android.os.Message msg) 
            switch (msg.what) 
                case UPDATE_TIME:
                    updateDateTime();
                    mHandler.sendEmptyMessageDelayed(UPDATE_TIME, 10 * 1000);
                    break;
            
        ;
    ;
    
    public TimeAndDateView(Context context, AttributeSet attrs, int defStyle) 
        super(context, attrs, defStyle);
        rootView = LayoutInflater.from(context).inflate(R.layout.time_date_layout, this, true);
        tvDate = (TextView)rootView.findViewById(R.id.date_textview);
        tvTime = (TextView)rootView.findViewById(R.id.time_textview);
        currentTime = "HH:mm";
        currentDate = "MM/dd";
        currentWeek = "EEEE";
    
    
    public TimeAndDateView(Context context, AttributeSet attrs) 
        super(context, attrs);
        rootView = LayoutInflater.from(context).inflate(R.layout.time_date_layout, this, true);
        tvDate = (TextView)rootView.findViewById(R.id.date_textview);
        tvTime = (TextView)rootView.findViewById(R.id.time_textview);
        currentTime = "HH:mm";
        currentDate = "MM/dd";
        currentWeek = "EEEE";
    
    
    public TimeAndDateView(Context context) 
        super(context);
        rootView = LayoutInflater.from(context).inflate(R.layout.time_date_layout, this, true);
        tvDate = (TextView)rootView.findViewById(R.id.date_textview);
        tvTime = (TextView)rootView.findViewById(R.id.time_textview);
        currentTime = "HH:mm";
        currentDate = "MM/dd";
        currentWeek = "EEEE";
    
    
    public void start() 
        if (mHandler.hasMessages(UPDATE_TIME)) 
            mHandler.removeMessages(UPDATE_TIME);
        
        mHandler.sendEmptyMessage(UPDATE_TIME);
    
    
    public void stop() 
        mHandler.removeMessages(UPDATE_TIME);
    
    
    private void updateDateTime() 
        Date date = SynchServerTimer.getDate();
        tvTime.setText(SmartLunznDate.getChinaDateStr(currentTime, date));
        tvDate.setText(SmartLunznDate.getChinaDateStr(currentDate, date) + " "
            + SmartLunznDate.getChinaDateStr(currentWeek, date));
    

以上是关于View第一部分自定义View 简单介绍及使用的主要内容,如果未能解决你的问题,请参考以下文章

Android 自定义 view—— onDraw

DRF之REST规范介绍及View请求流程分析

Android 自定义View

Android 自定义View

android自定义View: 饼状图绘制

android自定义View: 饼状图绘制