學習自定義 —— 自定義線型圖控制元件
阿新 • • 發佈:2018-12-14
自定義控制元件篇:
自定義折線圖
前言:自定義控制元件永遠都是客戶端開發的一個必須攻破的難題
首先得了解需要實現的樣式,確定有沒有可繼承的控制元件類,若沒有就直接繼承View。
View也有自己的生命週期,先了解下我們常用的幾個自定義View方法:
構造方法 - onMeasure - onLayout - onDraw - onTouchEvent - onAnimationStart
在構造方法裡進行畫圖工具和資料的初始化
srceenWidth = Tool.getWidth(context); srceenHeight = Tool.getHeight(context); paint = new Paint(); paint.setStyle(Paint.Style.FILL); paint.setAntiAlias(true); //去鋸齒 paint.setStrokeWidth(2); paint.setTextSize(context.getResources().getDimensionPixelSize(R.dimen.font_super_smallest)); paint.setColor(context.getResources().getColor(R.color._7C7F99)); String text = "08-06"; Rect rect = new Rect(); paint.getTextBounds(text,0,text.length(), rect); textheight = rect.height(); textWidth = rect.width(); int srceenHeightpf = srceenHeight/100; YPoint = Tool.dpToPx(10); YLength = srceenHeightpf*PriceShjListKinView.Ypinf -YPoint;//K線高度縮小給上下留白 YPoint = YPoint -(YPoint/2);//上下各一半 YScale = YLength/YScalenum;//Y軸均分
在onMeasure方法裡對View的高度和寬度做螢幕適配
setMeasuredDimension(srceenWidth, Tool.getHeight(context)/3);
在onLayout中可以獲取到View中的子View進行高寬的動態變更
在onDraw中進行繪圖:重點
protected void onDraw(Canvas canvas) { super.onDraw(canvas); this.setBackgroundColor(getResources().getColor(R.color.trade_tab)); //新增X軸刻度和文字 drawYklin(canvas); //新增X軸刻度和文字 drawXklin(canvas); /*早盤價折線*/ painm = new Paint(); painm.setStyle(Paint.Style.FILL); painm.setAntiAlias(true); //去鋸齒 painm.setStrokeWidth(2); painm.setTextSize(context.getResources().getDimensionPixelSize(R.dimen.font_super_smallest)); painm.setColor(context.getResources().getColor(R.color._DE9724)); if(coordinatecoyp==null){ coordinatecoyp = new ArrayList<>(); }else { coordinatecoyp.clear(); } int YMax = YLength - (YScalenum-1) * YScale; float KinMax = YLength - (YLength-YScale) * Tool.getSHJcalPrice(upPrice,downPrice,upPrice-downPrice); float C = KinMax - YMax; if(Mpricelist!=null){ for(int i=0; i<Mpricelist.size(); i++){ if(i!=Mpricelist.size()-1){ float y = YLength - (YLength-YScale+C) * Tool.getSHJcalPrice(Mpricelist.get(i).getYlin(),downPrice,upPrice-downPrice); float y1 = YLength - (YLength-YScale+C) * Tool.getSHJcalPrice(Mpricelist.get(i+1).getYlin(),downPrice,upPrice-downPrice); canvas.drawLine( i * XScale + textWidth/2 + addXwidth,y- YScale/2- textheight/2, (i+1) * XScale+ textWidth/2 + addXwidth,y1- YScale/2- textheight/2, painm); } for(SHjKinBean sHjKinBean: coordinate){ if(sHjKinBean.getXlin().equals(Mpricelist.get(i).getXlin())&&sHjKinBean.getCoordinateX()==COORDINATEM){ SHjKinBean sHjKinBean1 = new SHjKinBean(); sHjKinBean1.setCoordinateY(YLength - (YLength-XScale) * Tool.getSHJcalPrice(Mpricelist.get(i).getYlin(),downPrice,upPrice-downPrice)); sHjKinBean1.setCoordinateX( i * XScale+ textWidth/2 + addXwidth); sHjKinBean1.setXlin(Mpricelist.get(i).getXlin()); coordinatecoyp.add(sHjKinBean1); } } } } /*午盤價折線*/ paina = new Paint(); paina.setStyle(Paint.Style.FILL); paina.setAntiAlias(true); //去鋸齒 paina.setStrokeWidth(2); paina.setTextSize(context.getResources().getDimensionPixelSize(R.dimen.font_super_smallest)); paina.setColor(context.getResources().getColor(R.color._10A8F6)); if(Apricelist!=null){ for(int i=0; i<Apricelist.size(); i++){ if(i!=Apricelist.size()-1){ float y = YLength - (YLength-YScale+C) * Tool.getSHJcalPrice(Apricelist.get(i).getYlin(),downPrice,upPrice-downPrice); float y1 = YLength - (YLength-YScale+C) * Tool.getSHJcalPrice(Apricelist.get(i+1).getYlin(),downPrice,upPrice-downPrice); canvas.drawLine( i * XScale + textWidth/2 + addXwidth,y- YScale/2- textheight/2, (i+1) * XScale + textWidth/2 + addXwidth,y1- YScale/2- textheight/2, paina); } for(SHjKinBean sHjKinBean: coordinate){ if(sHjKinBean.getXlin().equals(Apricelist.get(i).getXlin())&&sHjKinBean.getCoordinateX()==COORDINATEA){ SHjKinBean sHjKinBean1 = new SHjKinBean(); sHjKinBean1.setCoordinateY(YLength - (YLength-XScale) * Tool.getSHJcalPrice(Apricelist.get(i).getYlin(),downPrice,upPrice-downPrice)); sHjKinBean1.setCoordinateX( i * XScale+ textWidth/2 + addXwidth); sHjKinBean1.setXlin(Apricelist.get(i).getXlin()); coordinatecoyp.add(sHjKinBean1); } } } } //畫點選彈出的詳情 if (needPop&&coordinatecoyp.size()>0){ drawPopView(popX,popY,canvas); } }
如果資料變更,我們提供一個供資料返回後頁面呼叫的方法進行View的資料變化
public void PirceSetShjKinData(List<SHJBean> shjlist){ this.shjlist = shjlist; UpteInvaldata(); invalidate(); } public void UpteInvaldata(){ upPrice = 0; downPrice = 0; boolean isfrist= true; Mpricelist = new ArrayList<>(); Apricelist = new ArrayList<>(); coordinate = new ArrayList<>();//儲存同一天價格高的xy軸座標做 String coordinatedata = ""; for(SHJBean shjBeans:shjlist){ try { float coordinatepriceM = -9999 , coordinatepriceA = -9999 ; float coordinateis = COORDINATEM;//預設早盤 String price = shjBeans.getMprice()!=null?shjBeans.getMprice():shjBeans.getAprice(); if(isfrist){//最低值需要拿第一個值 downPrice = Tool.getSHJDouble2(Double.valueOf(price)); isfrist = false; } coordinatedata = shjBeans.getData(); Mpricelist.add(new SHjKinBean(Tool.getSHJDouble2(Double.valueOf(shjBeans.getMprice())),shjBeans.getData())); Apricelist.add(new SHjKinBean(Tool.getSHJDouble2(Double.valueOf(shjBeans.getAprice())),shjBeans.getData())); upPrice = upPrice>Tool.getSHJDouble2(Double.valueOf(price))? upPrice:Tool.getSHJDouble2(Double.valueOf(price));//取出最高值 downPrice = downPrice < Tool.getSHJDouble2(Double.valueOf(price))? downPrice:Tool.getSHJDouble2(Double.valueOf(price));//取出最低值 if(shjBeans.getMprice()!=null){ coordinatepriceM = Tool.getSHJDouble2(Double.valueOf(price)); }else { coordinatepriceA = Tool.getSHJDouble2(Double.valueOf(price)); } /*拿出同一天最高價格是午盤還是早盤*/ coordinateis = coordinatepriceM>coordinatepriceA?COORDINATEM:COORDINATEA; SHjKinBean KinBean = new SHjKinBean(); KinBean.setCoordinateX(coordinateis); KinBean.setXlin(coordinatedata); coordinate.add(KinBean); }catch (Exception e){ e.printStackTrace(); } } XLength = srceenWidth-Tool.dpToPx(45); XScale = (XLength-(int) textWidth/2)/shjlist.size(); if(shjlist.size()<5){ addXwidth = XScale/2; }else { addXwidth = 0; } YLabel = new String[YScalenum]; float Ypricepf = Tool.getSHJcalPrice(upPrice,downPrice,YScalenum-1);//均分-1 for(int i=0;i<YScalenum;i++){ float Ypriceft = Tool.getSHJDouble2(Ypricepf*i)+downPrice; DecimalFormat decimalFormat = new DecimalFormat("0.00"); YLabel[i] = decimalFormat.format(Ypriceft); } XLabel = new String[shjlist.size()]; for(int i=0;i<XLabel.length;i++){ XLabel[i] = shjlist.get(i).getData(); } }
更新繪製需要呼叫invalidate();如果沒有在UI執行緒裡進行,則需要postInvalidate();
定義一個變數通知佈局點選顯示區域
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
if(MOVENUM==0){
downX = event.getX();
}
MOVENUM++;
break;
case MotionEvent.ACTION_MOVE:
if(needPop){
isMOVE = true;
popX = event.getX();
popY = event.getY();
invalidate();
}
break;
case MotionEvent.ACTION_UP:
if(!needPop&&!isMOVE){
needPop = !needPop;
}else {
float X = downX>event.getX()?downX-event.getX():event.getX()-downX;
if(X<=Tool.dpToPx(15)&&MOVENUM>1){
MOVENUM=0;
needPop = !needPop;
isMOVE = false;
}
}
popX = event.getX();
popY = event.getY();
invalidate();
downX = event.getX();
break;
}
return true;
}
同樣,點選後需要通知View呼叫invalidate進行佈局更新
點選後的手指區域處理程式碼:
float coorx;//最小的點選差比
int popWith = 0;
private void drawPopView(float x,float y,Canvas canvas){
int position=0;//最小的x軸
popWith = Tool.dpToPx(15);
for(int i=0;i<coordinatecoyp.size();i++){
if(i==0){
coorx = coordinatecoyp.get(i).getCoordinateX()>x?coordinatecoyp.get(i).getCoordinateX()-x:x-coordinatecoyp.get(i).getCoordinateX();
}
float coorxcopy = coordinatecoyp.get(i).getCoordinateX()>x?coordinatecoyp.get(i).getCoordinateX()-x:x-coordinatecoyp.get(i).getCoordinateX();
if(coorx>coorxcopy){
position = i;
coorx = coorxcopy;
}
}
x = coordinatecoyp.get(position).getCoordinateX();
Paint paintzb = new Paint();//豎線
paintzb.setStyle(Paint.Style.FILL);
paintzb.setAntiAlias(true); //去鋸齒
paintzb.setStrokeWidth(1);
paintzb.setTextSize(context.getResources().getDimensionPixelSize(R.dimen.font_super_smallest));
paintzb.setColor(context.getResources().getColor(R.color.text_usually));
float newx;
if(x>XLength){
newx = XLength;
}else {
newx = x;
}
canvas.drawLine(newx, YLength - YScale/2- textheight/2, newx, YLength - Ypoplin * YScale- YScale/2- textheight/2, paintzb);
// y = coordinatecoyp.get(position).getCoordinateY();
Paint painapop = new Paint();
painapop.setStyle(Paint.Style.STROKE);
painapop.setAntiAlias(true); //去鋸齒
painapop.setStrokeWidth(2);
painapop.setColor(context.getResources().getColor(R.color.text_usually));
Path path= new Path();
if(y-popWith*4 <10+ YPoint){
/*超出上邊框*/
y = (10+YPoint) - (y-popWith*4) +y;
}else if(y>YLength-YScale/2- textheight/2){
/*超出下邊框*/
float yend = y - YLength + textheight/2 + YScale/2;
y = y - yend;
}
String text = "交易日 :0101交易時間";
float width = paint.measureText(text);//文字的寬度
if(x+width>XLength){
/*超出右邊框*/
path.moveTo(x-popWith,y-popWith*4);
path.lineTo(x-popWith,y);
path.lineTo(x-width,y);
path.lineTo(x-width,y-popWith*4);
path.lineTo(x-popWith,y-popWith*4);
}else {
path.moveTo(x+popWith,y-popWith*4);
path.lineTo(x+popWith,y);
path.lineTo(x+width,y);
path.lineTo(x+width,y-popWith*4);
path.lineTo(x+popWith,y-popWith*4);
}
canvas.drawPath(path, painapop);
painapop.setStyle(Paint.Style.FILL);
painapop.setColor(context.getResources().getColor(R.color.trade_tab));
canvas.drawPath(path, painapop);
String data="- -",pricem="- -",pricea="- -";
for(int i=0;i<Mpricelist.size();i++){
if(Mpricelist.get(i).getXlin().equals(coordinatecoyp.get(position).getXlin())){
data = Mpricelist.get(i).getXlin();
pricem = ""+Mpricelist.get(i).getYlin();
}
}
for(int i=0;i<Apricelist.size();i++){
if(Apricelist.get(i).getXlin().equals(coordinatecoyp.get(position).getXlin())){
data = Apricelist.get(i).getXlin();
pricea = ""+Apricelist.get(i).getYlin();
}
}
int popHeight = (popWith*4)/8;
if(x+width>XLength){
/*超出右邊框*/
canvas.drawText("交易日:"+TimeDateUtil.changeDateFormat("yyyyMMdd", "MM-dd",data),x-width+Tool.dpToPx(10),y-popWith*4+popHeight*2+Tool.dpToPx(5),paint);
canvas.drawText("早盤價:"+ Utils.pricesFormatFloat(pricem),x-width+Tool.dpToPx(10),y-popWith*4+popHeight*4+Tool.dpToPx(5),painm);
canvas.drawText("午盤價:"+Utils.pricesFormatFloat(pricea),x-width+Tool.dpToPx(10),y-popWith*4+popHeight*6+Tool.dpToPx(5),paina);
}else {
canvas.drawText("交易日:"+TimeDateUtil.changeDateFormat("yyyyMMdd", "MM-dd",data),x+popWith+Tool.dpToPx(10),y-popWith*4+popHeight*2+Tool.dpToPx(5),paint);
canvas.drawText("早盤價:"+Utils.pricesFormatFloat(pricem),x+popWith+Tool.dpToPx(10),y-popWith*4+popHeight*4+Tool.dpToPx(5),painm);
canvas.drawText("午盤價:"+Utils.pricesFormatFloat(pricea),x+popWith+Tool.dpToPx(10),y-popWith*4+popHeight*6+Tool.dpToPx(5),paina);
}
}
整篇程式碼在下面貼出來