模仿QQ运动item的界面

首先按照老规矩,无图无真相嘛,先看看先:

效果图.gif

是不是很像呢,那具体是实现是怎样的呢,即使概括的来说就是
1.计算各个变量的值(记得是会随整个View的大小变化而变化)。
2其次利用好canvas.translate()这个方法,计算好大小移动canvas的原点。
3最后就是调用api提供的各种方法画图就是了。这么说是不是太过于简略了呢,好,现在就来

看看那具体的吧。首先看看xml有什么参数吧

 <com.example.jack.besselcurve.BesselCurveView 
    android:id="@+id/besselCurveView" 
    android:layout_width="match_parent" 
    android:layout_height="match_parent" 
    android:background="#ffffff" 
    android:layout_centerHorizontal="true" 
    app:besselColor="@color/besselColor" 
    app:besselColorText="@color/besselColorText" 
    app:friendAverageStep="6752" 
    app:averageStep="2603" 
    app:champion="Jack" 
    app:allStep="8765" 
    app:time="17:26" 
    app:ranking="15">
 </com.example.jack.besselcurve.BesselCurveView>

各参数对应的解释如下:

//时间 
private String time; 
//所有步数 
private int allStop; 
//还有平均步数 
private int friendAverageStep; 
//平均步数 
private int averageStep; 
//排名 
private String ranking; 
//头像 
private Bitmap champion_icon; 
//冠军名字 
private String champion;

接着代码段初始化所有参数:

TypedArray mTypedArray=context.getTheme().obtainStyledAttributes(attrs,R.styleable.BesselCurveView,defStyleAttr,0); 
int numCount=mTypedArray.getIndexCount(); 
for(int i=0;i<numCount;i++){ 
int attr=mTypedArray.getIndex(i); 
switch(attr){ 
     case R.styleable.BesselCurveView_allStep: 
     allStop=mTypedArray.getInt(attr,0); 
     break; 
case R.styleable.BesselCurveView_averageStep: 
     averageStep=mTypedArray.getInt(attr,0); 
     break; 
case R.styleable.BesselCurveView_friendAverageStep: 
     friendAverageStep = mTypedArray.getInt(attr,0); 
     break; 
case R.styleable.BesselCurveView_time: 
     time=mTypedArray.getString(attr); 
     break; 
case R.styleable.BesselCurveView_ranking: 
     ranking=mTypedArray.getString(attr); 
     break;
case R.styleable.BesselCurveView_champion: 
     champion=mTypedArray.getString(attr); 
     break; 
case R.styleable.BesselCurveView_besselColor: 
     mBesselCurveColor=mTypedArray.getColor(attr,Color.BLUE); 
     break; 
case R.styleable.BesselCurveView_besselColorText:
    besselColorText=mTypedArray.getColor(attr,Color.GRAY); break; 
  }
}

这些都是每个自定义都有的相当于模板,来初始化参数,都看的明白吧。接下来也很简单,就是初始化画笔等变量,以便于后面看画图更简单:

public void initValue(){ 
animSet=new AnimatorSet(); 
//外圆的画笔 
mCirclePaint=new Paint(Paint.ANTI_ALIAS_FLAG);
mCirclePaint.setStyle(Paint.Style.STROKE); 
mCirclePaint.setStrokeWidth(radius/10); 
mCirclePaint.setStrokeJoin(Paint.Join.ROUND);
mCirclePaint.setStrokeCap(Paint.Cap.ROUND); 
mCirclePaint.setAntiAlias(true); 
//中间的文字的画笔 
mCenterTextPaint=new Paint(); 
mCenterTextPaint.setColor(mBesselCurveColor); 
mCenterTextPaint.setTextSize(radius/5); 
mCenterTextPaint.setAntiAlias(true); 
//除中间之外的文字的画笔 
mTextPaint=new Paint(); 
mTextPaint.setAntiAlias(true); 
//最低下的矩形 
mBottomRectPaint=new Paint(Paint.ANTI_ALIAS_FLAG);
mBottomRectPaint.setColor(mBesselCurveColor); 
mBottomRectPaint.setAntiAlias(true); 
//虚线的画笔 
mDottedLinePaint = new Paint(); 
mDottedLinePaint.setAntiAlias(true); 
mDottedLinePaint.setStyle(Paint.Style.STROKE); 
mDottedLinePaint.setStrokeWidth(2); mDottedLinePaint.setColor(mBesselCurveColor); mDottedLinePaint.setPathEffect(new DashPathEffect(new float[]{5,5},1)); //画波浪线画笔 WavylinesPaint=new Paint(); WavylinesPaint = new Paint(Paint.ANTI_ALIAS_FLAG); WavylinesPaint.setColor(wavyColor); WavylinesPaint.setStyle(Paint.Style.FILL_AND_STROKE); //虚线的画线 mDottedLinePath=new Path(); 
//画波浪线画线 
WavyLinePath=new Path(); 
//底下更多的画线 
morePath=new Path(); 
mWaveCount = (int) Math.round(widthView / mWaveLength + 1.5);
marginBottomText=radius/4; 
}

好了,最重要的初始化都差不多了,现在就来画图(画画)吧先贴出所有画的代码然后再逐一讲解吧:

protected void onDraw(Canvas canvas) { 
super.onDraw(canvas); 
canvas.save(); 
canvas.translate(widthView/2,(heightView*((float)2/3))/2); 
//画内圆圈 
   mCirclePaint.setColor(besselColorText); 
   RectF mCircleRectF=new RectF(-radius,-radius,radius,radius); 
   canvas.drawArc(mCircleRectF,120,300,false,mCirclePaint); 
//画外圆圈 
   mCirclePaint.setColor(mBesselCurveColor); 
   canvas.drawArc(mCircleRectF,120,mCircleNum,false,mCirclePaint); 
//画中间的文字
   Rect mCenterRect=new Rect(); String tempAllStop=mCenterNum+""; 
   mCenterTextPaint.getTextBounds(tempAllStop,0,tempAllStop.length(),mCenterRect);
   int halfWidthText=(mCenterRect.right-mCenterRect.left)/2; 
   int halfHeightText=(mCenterRect.bottom-mCenterRect.top)/2; 
   canvas.drawText(tempAllStop,-halfWidthText,halfHeightText,mCenterTextPaint); 
//画上边的文字 
  mTextPaint.setColor(besselColorText); mTextPaint.setTextSize(radius/6); 
  String tempFriendAverageStep=stringTemplate(R.string.besselTime,time); 
  Rect mTopRect=new Rect();     mTextPaint.getTextBounds(tempFriendAverageStep,0,tempFriendAverageStep.length(),mTopRect); 
  int halfTopWidthText=(mTopRect.right-mTopRect.left)/2;    
  canvas.drawText(tempFriendAverageStep,-halfTopWidthText,-(halfHeightText+marginText),mTextPaint); 
//画下边的文字 String     
  tempAverageStep=stringTemplate(R.string.friendAverageStep,friendAverageStep+"");   
  Rect mBottomRect=new Rect(); 
 mTextPaint.getTextBounds(tempAverageStep,0,tempAverageStep.length(),mBottomRect);
  int halfBottomWidthText=(mBottomRect.right-mBottomRect.left)/2; 
  int mBottomHeightText=(mBottomRect.bottom-mBottomRect.top);
  canvas.drawText(tempAverageStep,-  halfBottomWidthText,mBottomHeightText+halfHeightText+marginText,mTextPaint); 
//画排名 Rect mNumRect=new Rect(); 
  mCenterTextPaint.getTextBounds(ranking,0,ranking.length(),mNumRect); 
  int halfNum=(mNumRect.right-mNumRect.left)/2; 
  mCenterTextPaint.setTextSize(40); canvas.drawText(ranking,-  halfNum,radius,mCenterTextPaint); 
  String rankingLeft=getContext().getResources().getString(R.string.ranking_left); 
  mTextPaint.getTextBounds(rankingLeft,0,rankingLeft.length(),mNumRect);
  canvas.drawText(rankingLeft,-halfNum-(mNumRect.right-  mNumRect.left)/2-20,radius,mTextPaint);   
canvas.drawText(getContext().getResources().getString(R.string.ranking_right),halfNum+10,radius,mTextPaint); 
canvas.restore(); 
//画最近七天和平均运动 
  mTextPaint.setTextSize(radius/9); canvas.save(); canvas.translate(0,heightView*((float)2/3));   
canvas.drawText(getContext().getResources().getString(R.string.nextSevenDay),marginLi neChart,0,mTextPaint); 
  Rect mPercentRect=new Rect(); 
  String mPercentText=stringTemplate(R.string.averageStep,averageStep+""); 
  mTextPaint.getTextBounds(mPercentText,0,mPercentText.length(),mPercentRect);
  canvas.drawText(mPercentText,widthView-marginLineChart-(mPercentRect.right-  mPercentRect.left),0,mTextPaint); 
//画虚线 
  mDottedLinePath.moveTo(marginLineChart,marginBottomText); 
  mDottedLinePath.lineTo(widthView-marginLineChart,marginBottomText); 
  canvas.drawPath(mDottedLinePath,mDottedLinePaint); 
//画7天数据柱状图 mTextPaint.setTextSize(radius/9);
   int lineWidth=(widthView-marginLineChart*2)/8; 
  mCalendar.setTime(new Date()); 
  RectF mRecf=null; 
  if(mListStep.size()>0){ 
  for(int i=mListStep.size();i>=1;i--){ 
  if(mListStep.get(i-1)!=0){ 
  int startX=marginLineChart+lineWidth*i-radius/23; 
  int endX=marginLineChart+lineWidth*i+radius/23; 
  if(mListStep.get(i-1)>mStandardStop){ 
//达标 mTextPaint.setColor(mBesselCurveColor); 
  int exceed=mListStep.get(i-1)-mStandardStop; 
  float standard=(float)
  (mCircleRectHeight*Double.valueOf(exceed/Double.valueOf(mStandardStop))); 
  mRecf=new RectF(startX,marginBottomText-(standard>mCircleRectHeight?mCircleRectHeight:standard) ,endX,marginBottomText+mCircleRectHeight);
  canvas.drawRoundRect(mRecf,50,50,mTextPaint); 
}else{ 
//不达标
  mTextPaint.setColor(besselColorText); 
  float noStandard=(float)(mCircleRectHeight*Double.valueOf(mListStep.get(i-1)/Double.valueOf(mStandardStop))); 
  mRecf=new RectF(startX,marginBottomText,endX,marginBottomText+(   noStandard>mCircleRectHeight?mCircleRectHeight:noStandard)); 
  canvas.drawRoundRect(mRecf,50,50,mTextPaint);
 }
 } 
//画底下的日期 
  mTextPaint.setColor(besselColorText); 
mCalendar.set(Calendar.DAY_OF_MONTH,mCalendar.get(Calendar.DAY_OF_MONTH)-1); 
  Rect rect =new Rect();
  String number=stringTemplate(R.string.day,mCalendar.get(Calendar.DAY_OF_MONTH)+"");
   mTextPaint.getTextBounds(number,0,number.length(),rect); 
   canvas.drawText(number,(marginLineChart+lineWidth*i)-(rect.right-rect.left)/2,marginBottomText+70,mTextPaint); 
} 
} 
  canvas.restore();
 //画波浪图形 
  canvas.save(); 
  float mWavyHeight=heightView*((float)4/5)+50; 
  canvas.translate(0,mWavyHeight); 
  WavyLinePath.reset(); 
  WavyLinePath.moveTo(-mWaveLength+ mOffset,0); 
  int wHeight=radius/5; 
  for(int i=0;i<mWaveCount;i++){ 
    WavyLinePath.quadTo((-mWaveLength*3/4)+(i*mWaveLength)+mOffset,wHeight,(-mWaveLength/2)+(i*mWaveLength)+mOffset,0); 
    WavyLinePath.quadTo((-mWaveLength/4)+(i * mWaveLength)+mOffset,- wHeight,i*mWaveLength+mOffset,0); 
} 
  WavyLinePath.lineTo(widthView,heightView-mWavyHeight); 
  WavyLinePath.lineTo(0,heightView-mWavyHeight);
  WavyLinePath.close();
  canvas.drawPath(WavyLinePath,WavylinesPaint); 
  canvas.restore(); 
//画最低的信息 
  float removeHeight=mWavyHeight+(radius/5); 
  canvas.translate(0,removeHeight); 
  float rectHeight=heightView-removeHeight; 
//画底下的矩形 
  RectF rect = new RectF(0,0,widthView,rectHeight); 
  canvas.drawRect(rect,mBottomRectPaint); 
//画头像 
  int bitmap_icon_x=radius/5;
  float centerHeight=rectHeight/2; 
  Bitmap bitmap_icon=getRoundCornerImage(champion_icon,50,radius/5,radius/5); 
  canvas.drawBitmap(bitmap_icon,bitmap_icon_x,centerHeight-  bitmap_icon.getHeight()/2,null);
  mTextPaint.setColor(Color.WHITE); mTextPaint.setTextSize(radius/8);
 //画冠军文字 
  int champion_x=radius/2; Rect mNameRect=new Rect(); 
  String championMame=stringTemplate(R.string.champion,champion); 
  mTextPaint.getTextBounds(championMame,0,championMame.length(),mNameRect); 
  canvas.drawText(championMame,champion_x,(rectHeight+(mNameRect.bottom-mNameRect.top))/2,mTextPaint); 
//画查看 
  String look=getContext().getResources().getString(R.string.check); 
  mTextPaint.getTextBounds(look,0,look.length(),mNameRect); 
  canvas.drawText(look,widthView-(radius*(float)2/3),(rectHeight+(mNameRect.bottom-mNameRect.top))/2,mTextPaint);
 //画更多图像 
  float morePoint=(radius*(float)2/3)/2; 
  canvas.drawLine(widthView-morePoint,centerHeight-(mNameRect.bottom-  mNameRect.top)/2, widthView-morePoint+15,centerHeight,mTextPaint);
  canvas.drawLine(widthView-morePoint+15,centerHeight,widthView-morePoint,    centerHeight+(mNameRect.bottom-mNameRect.top)/2,mTextPaint);
 }

代码是不是有点多呢,没办法画的东西本身就有点多了。好了刚开始我说要移动canvas的原点是不是,你看刚开始就移动了吧:

  super.onDraw(canvas);
  canvas.save();
  canvas.translate(widthView/2,(heightView*((float)2/3))/2);

1、移动原点到整个圆弧的中心,其中widthView是整个view的宽,heightView是整个view的高,如下图:


center.PNG

就在上图的蓝色点就是现在的原点。
然后在这原点里画圆弧呗,代码如下

 //画内圆圈 
mCirclePaint.setColor(besselColorText); 
RectF mCircleRectF=new RectF(-radius,-radius,radius,radius); 
canvas.drawArc(mCircleRectF,120,300,false,mCirclePaint);
 //画外圆圈 
mCirclePaint.setColor(mBesselCurveColor); 
canvas.drawArc(mCircleRectF,120,mCircleNum,false,mCirclePaint);

mCircleNum是为了实现动画效果的,这后面会讲,这样圆弧就画完了。效果也是如上图。
2.在中心点再画今天的走的总路程,代码如下:

 //画中间的文字 
Rect mCenterRect=new Rect(); 
String tempAllStop=mCenterNum+""; 
mCenterTextPaint.getTextBounds(tempAllStop,0,tempAllStop.length(),mCenterRect);
int halfWidthText=(mCenterRect.right-mCenterRect.left)/2; 
int halfHeightText=(mCenterRect.bottom-mCenterRect.top)/2; 
canvas.drawText(tempAllStop,-halfWidthText,halfHeightText,mCenterTextPaint);

基本的实现思路是用Rect在这个类计算出你要画文字的大小,然后在原点画,不过,记得这里的x,y点是在原点的左下,具体详解看这里写链接内容
接这就是画时间和好友平均步数,其实实现原理也是一样的,只不过在上面的高度是

canvas.drawText(tempFriendAverageStep,-halfTopWidthText,-(halfHeightText+marginText),mTextPaint);

是中心总步数高度的一半再加间隔,而下面的是:

canvas.drawText(tempAverageStep,-halfBottomWidthText,mBottomHeightText+halfHeightText+marginText,mTextPaint);

是下面文字总的高度再加上中心总步数高度的一半再加间隔。现在效果如下图:

img1.PNG

接着就是画排名,首先还是套路:

Rect mNumRect=new Rect(); 
mCenterTextPaint.getTextBounds(ranking,0,ranking.length(),mNumRect); 
int halfNum=(mNumRect.right-mNumRect.left)/2; 
mCenterTextPaint.setTextSize(40); 
canvas.drawText(ranking,-halfNum,radius,mCenterTextPaint);

计算出排名文字的大小,然后在中心原点x轴为排名文字的一半,y轴问为半径画出排名,效果图如下:

img2.PNG

接着就在排名的两端画文字就行了,带代码如下:

String rankingLeft=getContext().getResources().getString(R.string.ranking_left); 
mTextPaint.getTextBounds(rankingLeft,0,rankingLeft.length(),mNumRect); 
canvas.drawText(rankingLeft,-halfNum-(mNumRect.right-mNumRect.left)/2-20,radius,mTextPaint); 
canvas.drawText(getContext().getResources().getString(R.string.ranking_right),halfNum+10,radius,mTextPaint); 

思路还是一样,就不说了。此时效果

img3.PNG

画底下柱状图是,首先用canvas.restore();恢复原点到(0,0)的状态,再用canvas.translate(0,heightView*((float)2/3));把原点移动到圆弧的下面,接着又可以继续画,实现思路和前面一样:

//画最近七天和平均运动 
mTextPaint.setTextSize(radius/9); 
canvas.save(); canvas.translate(0,heightView*((float)2/3)); 
canvas.drawText(getContext().getResources().getString(R.string.nextSevenDay),marginLineChart,0,mTextPaint); 
Rect mPercentRect=new Rect();
 String mPercentText=stringTemplate(R.string.averageStep,averageStep+""); 
mTextPaint.getTextBounds(mPercentText,0,mPercentText.length(),mPercentRect); 
canvas.drawText(mPercentText,widthView-marginLineChart-(mPercentRect.right-mPercentRect.left),0,mTextPaint); 
//画虚线
 mDottedLinePath.moveTo(marginLineChart,marginBottomText); 
mDottedLinePath.lineTo(widthView-marginLineChart,marginBottomText); 
canvas.drawPath(mDottedLinePath,mDottedLinePaint);

此时效果如下:

img4.PNG

接下来画柱状图,首先int lineWidth=(widthView-marginLineChart*2)/8;计算出每个点之间的间隔

img5.PNG
if(mListStep.size()>0){ 
  for(int i=mListStep.size();i>=1;i--){ 
    if(mListStep.get(i-1)!=0){ 
    //计算出起始点X和终点X的值 
     int startX=marginLineChart+lineWidth*i-radius/23; 
     int endX=marginLineChart+lineWidth*i+radius/23; 
    if(mListStep.get(i-1)>mStandardStop){ 
   //达标 mTextPaint.setColor(mBesselCurveColor); 
   //超出的部分  
     int exceed=mListStep.get(i-1)-mStandardStop;
  //算出柱体大小 float standard=(float)   (mCircleRectHeight*Double.valueOf(exceed/Double.valueOf(mStandardStop))); 
    mRecf=new RectF(startX,marginBottomText-(standard>mCircleRectHeight?mCircleRectHeight:standard) ,endX,marginBottomText+mCircleRectHeight); 
   canvas.drawRoundRect(mRecf,50,50,mTextPaint); 
}else{ 
  //不达标 
   mTextPaint.setColor(besselColorText); 
 //算出不达标柱体的大小 
  float noStandard=(float)(mCircleRectHeight*Double.valueOf(mListStep.get(i-1)/Double.valueOf(mStandardStop))); 
   mRecf=new RectF(startX,marginBottomText,endX,marginBottomText+(   noStandard>mCircleRectHeight?mCircleRectHeight:noStandard)); 
   canvas.drawRoundRect(mRecf,50,50,mTextPaint); 
}
} 
 //画底下的日期 
   mTextPaint.setColor(besselColorText); 
mCalendar.set(Calendar.DAY_OF_MONTH,mCalendar.get(Calendar.DAY_OF_MONTH)-1); 
   Rect rect =new Rect(); 
   String number=stringTemplate(R.string.day,mCalendar.get(Calendar.DAY_OF_MONTH)+"");
   mTextPaint.getTextBounds(number,0,number.length(),rect); 
   canvas.drawText(number,(marginLineChart+lineWidth*i)-(rect.right-rect.left)/2,marginBottomText+70,mTextPaint); 
} 
}

mStandardStop是达标的数据,当数据小于mStandardStop就是不达标,所以柱状图就要画在虚线的下面,mCircleRectHeight是柱状图一半的高
float standard=(float)(mCircleRectHeight*Double.valueOf(exceed/Double.valueOf(mStandardStop)));这句代码是计算出下面圆柱体的具体大小,noStandard>mCircleRectHeight?mCircleRectHeight:noStandard当,但柱状图大于mCircleRectHeight时就用mCircleRectHeight不然就根据计算的数值来。当数据大于mStandardStop时,
int exceed=mListStep.get(i-1)-mStandardStop;float standard=(float)(mCircleRectHeight*Double.valueOf(exceed/Double.valueOf(mStandardStop)));exceed是计算出超出的部分,再拿超出的部分算出具体的大小,剩下的和小于的一样,当standard大于最大的mCircleRectHeight是就用mCircleRectHeight否则就用standard。底下日期是用Calendar得到前7天的日期再循环的画上去,思路和上面一样不再赘述。此时效果如下:

img6.PNG

接下来是画波浪,画波浪是用了贝塞尔曲线的方法画的,如果不懂贝塞尔曲线请参考这里写链接内容,这也是我学贝塞尔曲线参考的内容。首先我们又把canvas恢复到原点canvas.restore();再用float mWavyHeight=heightView*((float)4/5)+50; canvas.translate(0,mWavyHeight);移动这个位置,是为了适配。

WavyLinePath.reset(); 
WavyLinePath.moveTo(-mWaveLength+ mOffset,0); 
int wHeight=radius/5; for(int i=0;i<mWaveCount;i++){ 
WavyLinePath.quadTo((-mWaveLength*3/4)+(i*mWaveLength)+mOffset,wHeight,(-mWaveLength/2)+(i*mWaveLength)+mOffset,0); 
WavyLinePath.quadTo((-mWaveLength/4)+(i * mWaveLength)+mOffset,-wHeight,i*mWaveLength+mOffset,0); 
} 
WavyLinePath.lineTo(widthView,heightView-mWavyHeight); 
WavyLinePath.lineTo(0,heightView-mWavyHeight); 
WavyLinePath.close(); 
canvas.drawPath(WavyLinePath,WavylinesPaint);

WavyLinePath.quadTo就是贝塞尔曲线调的方法,for循环几次使之形成波浪图形,记得一样要WavyLinePath.lineTo().不让会出现底下有些地方会画不到。原理是向上定一个控制点有向下定一个控制点使之形成一个sin函数图形。具体请学贝塞尔曲线。此时效果图:

img7.PNG

最后就是画底下的矩形和头像和文字了。最值得讲的是头像我一开始的设想的传Url的,不过这样子又要做网络方面的代码工作,这样子会破怀类的功能单一性原则,所以最后我实在外部传一个位图,在位图进行处理使其圆角。剩下的只是画文字而已,上面已经讲够多了,就不在讲了。
对了,最后还有一个刚开始的动画效果。

public void startAnimator(){ 
     ValueAnimator mCircleAminator=ValueAnimator.ofFloat(0f,300f); 
     mCircleAminator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { 
     mCircleNum=(float)animation.getAnimatedValue(); postInvalidate();
 } 
}); 
   ValueAnimator mCenterText=ValueAnimator.ofInt(0,allStop); 
mCenterText.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { 
mCenterNum=(int)animation.getAnimatedValue(); postInvalidate(); 
} }); 
ValueAnimator mWavyAnimator = ValueAnimator.ofInt(0, mWaveLength); 
mWavyAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { 
mOffset = (int) animation.getAnimatedValue(); postInvalidate(); 
} }); 
animSet.setDuration(2000); 
animSet.playTogether(mCircleAminator,mCenterText,mWavyAnimator); 
animSet.start(); 
}
 //字符串拼接 
public String stringTemplate(int template,String content){ 
return String.format(getContext().getResources().getString(template),content);
 }

其实也简单通过设置ValueAnimator让它在规定的时间内产生数值的变化,再调用postInvalidate().对View的界面进行刷新即可实现动画效果。

最后给源码好好研究吧源码只有好好看源码才能学到更多东西。

如果对你有帮助就请给我给星星或喜欢吧。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,332评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,508评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,812评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,607评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,728评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,919评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,071评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,802评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,256评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,576评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,712评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,389评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,032评论 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,798评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,026评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,473评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,606评论 2 350

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,837评论 25 707
  • 系列文章之 Android中自定义View(一)系列文章之 Android中自定义View(二)系列文章之 And...
    YoungerDev阅读 4,387评论 3 11
  • 自定义控件教程: 1,http://blog.csdn.net/aigestudio/article/detail...
    CoderGC阅读 1,652评论 1 11
  • 昨天公司组织培训,内容是打造狼性团队。彪哥和端哥都是组长,他们关系很好,端哥是我组长。培训老师要大家做游戏,用面...
    Mokiil阅读 272评论 0 1
  • 这几天休息的不是很好,究其原因我发现是自己对自己的表现不满意。我在意师傅的批评,也反感自己的幼稚和不走心。熬夜看完...
    叹谁逍遥阅读 138评论 0 0