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

    xiaoxiao2025-11-20  8

    自定义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)); } }
    转载请注明原文地址: https://ju.6miu.com/read-1304378.html
    最新回复(0)