自定义View
Android给我们提供了常用组件,然而随着开发的深入,这些组件渐渐无法满足我们各式各样的需求,此时就需要我们在已有的组件上创建新的功能,甚至是直接自己写一个新的View控件,来满足自己的需要。这就是我们常说的自定义View。
在自定义View时候,我们常常会重写onDraw()方法来重新绘制我们的控件;当该控件需要用wrap_content属性时候,还需要用到onMearsure()方法来重新测量;以及需要一些特殊样式时候,还可以通过修改attrs.xml(或者写其他xml)来设置控件属性。
在View中一些重要的回调方法有:
onFinishInflate():加载完XML组件后回调。
onSizeChanged():当组件大小变化时回调。
onMearsure():通过回调该方法进行控件测量。
onLayout():通过回调该方法确定控件显示的位置。
onTouchEvent():监听到触摸事件后回调。
通常情况下,我们用三种方法来实现自定义的控件:
l 对现有控件的扩展;
l 通过组合方式实现新的控件;
l 通过重写View实现新的控件。
1.对现有控件的扩展
对现有控件的扩展指的是在Android原生控件的基础上进行一些功能扩展。一般情况下都是在进行重写onDraw方法来实现。
以TextView为例,我们想在写的字下面加上两层背景,再让字实现闪烁滚动的效果。此时就可以先自定义View继承TextView类,然后可以重写onDraw()的方法。
注意下重写onDraw()方法时候,super.onDraw()方法的位置。这个方法是继承于父类,对于TextView来说就是显示文字。所以我们需要将绘制背景写在super.onDraw()方法前,而将字体闪烁设置写在super.onDraw()方法后。如下所示:
@Override protected void onDraw(Canvas canvas) { canvas.drawRect(0,0,getMeasuredWidth()+10,getMeasuredHeight()+10,mPaint1); canvas.drawRect(10,10,getMeasuredWidth()-10,getMeasuredHeight()-10,mPaint2); canvas.save(); canvas.translate(10,0); super.onDraw(canvas); canvas.restore(); if(matrix!=null){ mTranslate+=mViewwidth/5; if(mTranslate>2*mViewwidth){ mTranslate=-mViewwidth; } matrix.setTranslate(mTranslate,0); linearGradient.setLocalMatrix(matrix); postInvalidateDelayed(100); } }完整的代码为:
public class TestTextView extends TextView { private Paint mPaint; private Paint mPaint1; private Paint mPaint2; private int mTranslate; private int mViewwidth=0; private Matrix matrix; private LinearGradient linearGradient; public TestTextView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); init(context); } public TestTextView(Context context, AttributeSet attrs) { super(context, attrs); init(context); } public TestTextView(Context context) { super(context); init(context); } @Override protected void onDraw(Canvas canvas) { canvas.drawRect(0,0,getMeasuredWidth()+10,getMeasuredHeight()+10,mPaint1); canvas.drawRect(10,10,getMeasuredWidth()-10,getMeasuredHeight()-10,mPaint2); canvas.save(); canvas.translate(10,0); super.onDraw(canvas); canvas.restore(); if(matrix!=null){ mTranslate+=mViewwidth/5; if(mTranslate>2*mViewwidth){ mTranslate=-mViewwidth; } matrix.setTranslate(mTranslate,0); linearGradient.setLocalMatrix(matrix); postInvalidateDelayed(100); } } @Override protected void onSizeChanged(int w,int h,int oldw,int oldh){ super.onSizeChanged(w, h, oldw, oldh); if(mViewwidth==0){ mViewwidth=getMeasuredWidth(); if(mViewwidth>0){ mPaint=getPaint(); linearGradient=new LinearGradient(0,0,mViewwidth,0,new int[]{Color.BLUE,0xffffffff,Color.BLUE},null, Shader.TileMode.CLAMP); mPaint.setShader(linearGradient); matrix=new Matrix(); } } } private void init(Context context) { mPaint1=new Paint(); mPaint1.setColor(Color.BLUE); mPaint2=new Paint(); mPaint2.setColor(Color.RED); } }运行一下看看效果为:
2.通过组合方式实现新的控件
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent"> <RelativeLayout android:id="@+id/rl_title" android:layout_width="match_parent" android:layout_height="60dp" android:background="@color/blue"> <TextView android:id="@+id/tv_title_areacompareform" android:layout_width="wrap_content" android:layout_height="match_parent" android:layout_marginLeft="@dimen/sm_10" android:gravity="center_vertical" android:textColor="@color/white" android:text="标题"/> <TextView android:id="@+id/tv_unit" android:layout_width="wrap_content" android:layout_height="match_parent" android:layout_alignParentRight="true" android:layout_marginRight="@dimen/sm_20" android:gravity="center" android:text="单位:%" android:textColor="@color/white" /> </RelativeLayout> <RelativeLayout android:id="@+id/rl_linechartview" android:layout_below="@+id/rl_title" android:layout_width="match_parent" android:layout_height="match_parent"> <lecho.lib.hellocharts.view.LineChartView android:id="@+id/lcv_gdl" android:layout_width="match_parent" android:layout_marginTop="40dp" android:layout_marginBottom="30dp" android:layout_marginLeft="10dp" android:layout_marginRight="20dp" android:paddingLeft="5dp" android:paddingRight="25dp" android:paddingBottom="20dp" android:layout_height="match_parent"></lecho.lib.hellocharts.view.LineChartView> </RelativeLayout> </RelativeLayout>
然后在自定义View里填充并给这个控件里的各个子控件赋值,代码如下:
public GDLLineChartView(Context context, String title, List<GDLCompareBean> beanList) { super(context); this.context = context; view = LayoutInflater.from(context).inflate(R.layout.view_gdl_lcv, this); ButterKnife.bind(this, view); tvTitle.setText(title); // tvFormTitle.setText(title); initForm(beanList); } private void initForm( List<GDLCompareBean> beanList){ List<Line> lines = new ArrayList<Line>(); List<PointValue> values = new ArrayList<PointValue>(); List<AxisValue> axisValues = new ArrayList<AxisValue>(); for (int j = 0; j < beanList.size(); ++j) { values.add(new PointValue(j, Float.valueOf(beanList.get(j).getGdl()))); axisValues.add(new AxisValue(j).setLabel(beanList.get(j).getYear()));//添加X轴显示的刻度值 } Line line = new Line(values); LineChartValueFormatter formatter=new SimpleLineChartValueFormatter(2); line.setFormatter(formatter); line.setColor(0xFF0088a8); line.setShape(shape); line.setStrokeWidth(5);//设置折线宽度 line.setFilled(false);//设置折线覆盖区域颜色 line.setPointColor(Color.RED);//设置节点颜色 line.setPointRadius(5);//设置节点半径 line.setHasLabels(true);//是否显示节点数据 line.setHasLines(true);//是否显示折线 lines.add(line); data = new LineChartData(lines); data.setAxisXBottom(new Axis(axisValues).setHasLines(true)); data.setAxisYLeft(new Axis().setHasLines(true).setMaxLabelChars(3)); data.setBaseValue(20);//设置反向覆盖区域颜色 data.setValueLabelBackgroundAuto(false);//设置数据背景是否跟随节点颜色 data.setValueLabelBackgroundEnabled(false);//设置是否有数据背景 data.setValueLabelsTextColor(Color.RED);//设置数据文字颜色 data.setValueLabelTextSize(12);//设置数据文字大小 data.setValueLabelTypeface(Typeface.MONOSPACE);//设置数据文字样式 lcvGDL.setLineChartData(data); lcvGDL.setOnValueTouchListener(new LineChartOnValueSelectListener() { @Override public void onValueSelected(int lineIndex, int pointIndex, PointValue value) { Log.i("sss","sss"); if(gdlLineChartViewSelectedListener!=null){ gdlLineChartViewSelectedListener.onSelected((int)value.getX()); } } @Override public void onValueDeselected() { Log.i("sss","sss"); } }); }接下来暴露接口,以便调用者可以调用接口中相应的点击方法:
public void setOnSelectedListener(GDLLineChartViewSelectedListener gdlLineChartViewSelectedListener){ this.gdlLineChartViewSelectedListener=gdlLineChartViewSelectedListener; } public interface GDLLineChartViewSelectedListener{ void onSelected(int position); }调用时候只需如此调用即可:
GDLLineChartView lview = new GDLLineChartView(getContext(), MapTitle, gdlCompareBeanList); llLineChartView.addView(lview); lview.setOnSelectedListener(new GDLLineChartView.GDLLineChartViewSelectedListener() { @Override public void onSelected(int i) { SingleModuleBean bean = singleModuleBeanList.get(i); String[] titleList = bean.getTitles().split(","); String FormTitle = titleList[1]; String Year = bean.getYear() + "年"; FormTitle = Year + FormTitle; List<GDLBean> gdlBeanList = getList(bean.getTablejson()); GDLAreaFormView aview = new GDLAreaFormView(getContext(), FormTitle, gdlBeanList); llAreaCompareForm.removeAllViews(); llAreaCompareForm.addView(aview); } });效果图大概为:
3.通过重写View实现新的控件
当有时候需求原生控件里完全没有的控件时候,可以自己创建一个全新的自定义View来。这样的控件继承于View类,通过onDraw()来实现该View的绘制。以音频图为例子,音频图可以通过绘制一系列的矩形来实现,具体代码如下:
public class VoiceView extends View { private Paint mPaint; private int count=10; private int mRectWidth=60; private int mRectHeight=800; private int off=10; private int mWidth; public VoiceView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); init(context); } public VoiceView(Context context, AttributeSet attrs) { super(context, attrs); init(context); } public VoiceView(Context context) { super(context); init(context); } private void init(Context context){ mWidth=getMeasuredWidth(); mPaint=new Paint(); mPaint.setColor(Color.BLUE); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); for(int i=0;i<count;i++){ Double random=Math.random(); canvas.drawRect( (float)(mWidth*0.4/2+mRectWidth*i+off), (float)(random*mRectHeight), (float)(mWidth*0.4/2+mRectWidth*i+mRectWidth), mRectHeight, mPaint); } }这里我在onDraw()方法里绘制了10个宽度为50,高度为随机的蓝色矩形。然后创建一个activity,添加这个自定义View:
public class MainActivity extends AppCompatActivity { private VoiceView myView; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); myView = new VoiceView(this);//初始化自定义View this.setContentView(myView);//设置当前的用户界面 } }可以看看效果图为: