Heim  >  Artikel  >  Java  >  Implementierungsprinzip und Codeerklärung für Android-Rubbelkarten

Implementierungsprinzip und Codeerklärung für Android-Rubbelkarten

高洛峰
高洛峰Original
2017-01-17 10:17:061483Durchsuche

实现刮刮卡我们可以Get到哪些技能?

* 圆形圆角图片的实现原理
* 双缓冲技术绘图
* Bitmap获取像素值数据
* 获取绘制文本的长宽
* 自定义View的掌握
* 获取屏幕密度
* TypeValue.applyDemension
* Canvas的一些绘制方法
* Paint的一些常用的属性
* Path的一些方法

刮刮卡的实现原理图

Implementierungsprinzip und Codeerklärung für Android-Rubbelkarten

这里用到了13中模式中的DstOut这种模式。 
对于这幅图而言,首先绘制Dst,设置xfermode,再绘制Src。

刮刮卡的实现原理步骤

1.绘制显示中奖的文本 
2.绘制图层区/前景图片 
3.设置xfermode 
4.绘制Path 
5.清除所有图层区方案 
6.自定义属性

代码讲解

第一部分:绘制中奖信息

public class GuaGuaLe extends View{
 /**中奖文本变量*/
 private String mText;//显示文本
 private Paint mTextPaint;//绘制文本的画笔
 private Rect mTextBounds;//文本的边界
 
 public GuaGuaLe(Context context) {
 this(context, null);
 }
 
 public GuaGuaLe(Context context, AttributeSet attrs) {
 super(context, attrs);
 initView();
 }
 
 /**初始化一些实例变量*/
 private void initView() {
 setTextMessage();//实例化文本的一些变量
 
 }
 
 private void setTextMessage() {
 mText = "谢谢惠顾";
 mTextBounds = new Rect();
 mTextPaint = new Paint();
 mTextPaint.setAntiAlias(true);
 mTextPaint.setColor(Color.DKGRAY);
 mTextPaint.setTextSize(22);
 mTextPaint.getTextBounds(mText,0,mText.length(),mTextBounds);//获取到绘制文本的长宽
 }
 
 
 @Override
 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
 super.onMeasure(widthMeasureSpec, heightMeasureSpec);
 }
 
 @Override
 protected void onDraw(Canvas canvas) {
 //绘制文本
 canvas.drawText(mText,getWidth()/2 - mTextBounds.width()/2,getHeight()/2 + mTextBounds.height()/2,mTextPaint);
 }
}

如下图:

Implementierungsprinzip und Codeerklärung für Android-Rubbelkarten

第二部分:绘制图层区/前景图片

/**画板变量*/
private Canvas mCanvas;//缓冲画板
private Bitmap mLayerBimtap;//图层图片
private Bitmap mBitmap;//画板上的画纸
private Paint mPaint;//画笔
 
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
setCanvasMessage(getMeasuredWidth(),getMeasuredHeight());//实例化缓存画板的一些变量
}
 
/***
* 双缓冲技术
* 1.创建一个与屏幕绘制区域一致的Canvas对象
* 2.先将图形绘制到内存中
* 3.再一次性将对象上的图形拷贝到屏幕上
* @param width
* @param height
*/
private void setCanvasMessage(int width,int height) {
mPaint = new Paint();
mPaint.setAntiAlias(true);
mPaint.setStyle(Paint.Style.FILL);
mPaint.setStrokeWidth(3);
mPaint.setColor(Color.parseColor("#c0c0c0"));
mBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);//创建一个空白的位图
mCanvas = new Canvas(mBitmap);//在此Bitmap上作画
//mCanvas.drawColor(Color.parseColor("#c0c0c0"));//绘制背景颜色
mCanvas.drawRoundRect(new RectF(0, 0, width, height), 20, 20, mPaint);//绘制圆角矩形
mLayerBimtap = BitmapFactory.decodeResource(getResources(), R.mipmap.fg_guaguaka);
mCanvas.drawBitmap(mLayerBimtap, null, new RectF(0, 0, width, height), null);//绘制图层图片
 
@Override
protected void onDraw(Canvas canvas) {
//绘制文本
canvas.drawText(mText,getWidth()/2 - mTextBounds.width()/2,getHeight()/2 + mTextBounds.height()/2,mTextPaint);
//绘制图层
//canvas.drawBitmap(mBitmap,0,0,null);
//绘制图层图片
canvas.drawBitmap(mBitmap,0,0,null);
}
}

如下图(图层颜色):

Implementierungsprinzip und Codeerklärung für Android-Rubbelkarten

如下图(图层图片):

Implementierungsprinzip und Codeerklärung für Android-Rubbelkarten

第三部分:设置xfermode以及绘制Path

/**路径变量*/
private Path mPath;
private Paint mPathPaint;
private int mLastX;//记录上一次X坐标
private int mLastY;//记录上一次Y坐标
 
/**初始化一些实例变量*/
private void initView() {
setTextMessage();//实例化文本的一些变量
setPathMessage();//实例化路径的一些变量
}
 
@Override
public boolean onTouchEvent(MotionEvent event) {
int action = event.getAction();
//获取手指在屏幕上的坐标
int x = (int) event.getX();
int y = (int) event.getY();
switch (action){
 case MotionEvent.ACTION_DOWN:
 mLastX = x;
 mLastY = y;
 mPath.moveTo(mLastX,mLastY);
 break;
 case MotionEvent.ACTION_MOVE:
 int dx = Math.abs(x - mLastX);
 int dy = Math.abs(y - mLastY);
 if(dx > 3 || dy >3){
  mPath.lineTo(x, y);
 }
 mLastX = x;
 mLastY = y;
 break;
 case MotionEvent.ACTION_UP:
 break;
 default:
 break;
}
invalidate();//主线程中刷新
return true;//截取事件向下分发
}
 
@Override
protected void onDraw(Canvas canvas) {
//绘制文本
canvas.drawText(mText,getWidth()/2 - mTextBounds.width()/2,getHeight()/2 + mTextBounds.height()/2,mTextPaint);
//绘制路径
drawPath();
//绘制图片【图层背景颜色】
//canvas.drawBitmap(mBitmap,0,0,null);
//绘制图片【图层图片】
canvas.drawBitmap(mBitmap, 0, 0, null);
 
}
 
//返回给系统的canvas是一幅带有路径的位图
private void drawPath() {
//这之前是设置了图层图片
mPathPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_OUT));
mCanvas.drawPath(mPath,mPathPaint);
}

如下图 

Implementierungsprinzip und Codeerklärung für Android-Rubbelkarten

第三部分:清除所有图层区方案

/**擦除是否完成*/
 private volatile boolean isComplete;
 
 /**回调接口*/
 private OnCompleteListener mOnCompleteListener;
 
 
 public interface OnCompleteListener{
 void complete();
 }
 
 //对外设置调用的公开方法
 public void setmOnCompleteListener(OnCompleteListener mOnCompleteListener){
 this.mOnCompleteListener = mOnCompleteListener;
 }
 
 case MotionEvent.ACTION_UP:
  //开启一个线程去计算擦除的面积
  new Thread(new Runnable() {
   @Override
   public void run() {
   int width = getWidth();
   int height = getHeight();
   int area = 0;//记录被擦数的范围大小
   int percent ;
   for(int i=0;i<width;i++)
    for(int j = 0;j<height;j++){
    if(mBitmap.getPixel(i,j) == 0){
     area ++;
    }
    }
 
   if(area > 0){
    percent = area *100/(width*height);
    if(percent > 20){
    isComplete = true;
    postInvalidate();//在子线程中刷新,可能比较耗时操作
    }
   }
   }
  }).start();
 
 @Override
 protected void onDraw(Canvas canvas) {
 //绘制文本
 canvas.drawText(mText, getWidth() / 2 - mTextBounds.width() / 2, getHeight() / 2 + mTextBounds.height() / 2, mTextPaint);
 
 if(isComplete){
  if(mOnCompleteListener != null){
  mOnCompleteListener.complete();//接口回调,在主线程中
  }
 }
 
 if(!isComplete){
  //绘制路径
  drawPath();
  //绘制图片【图层背景颜色】
  //canvas.drawBitmap(mBitmap,0,0,null);
  //绘制图片【图层图片】
  canvas.drawBitmap(mBitmap, 0, 0, null);
 }
 }
 
 
public class MainActivity extends AppCompatActivity {
 
 @Override
 protected void onCreate(Bundle savedInstanceState) {
 super.onCreate(savedInstanceState);
 setContentView(R.layout.activity_main);
 GuaGuaLe guaguaLeView = (GuaGuaLe) findViewById(R.id.guaguale);
 //注册了一个观察者
 guaguaLeView.setmOnCompleteListener(new GuaGuaLe.OnCompleteListener() {
  @Override
  public void complete() {
  Toast.makeText(MainActivity.this, "用户绘制了大部分", Toast.LENGTH_SHORT).show();
  }
 });
 }
}

如下图:

Implementierungsprinzip und Codeerklärung für Android-Rubbelkarten

第五部分:自定义View的属性

在values/attrs.xml中定义
 <declare-styleable name="GuaGuaLe">
 <attr name="text" format="string"></attr>
 <attr name="textSize" format="dimension"></attr>
 <attr name="textColor" format="color"></attr>
 </declare-styleable>
 
在布局文件中定义属性
 <zlll.bg.com.example.cn.myapplication.view.GuaGuaLe
  android:id="@+id/guaguale"
  android:layout_width="350dp"
  android:layout_height="200dp"
  app:text="$5,000"
  app:textColor="#c02040"
  app:textSize="22sp"
  android:layout_centerInParent="true"/>
 
在主界面中定义Text文本
public class MainActivity extends AppCompatActivity {
 
 @Override
 protected void onCreate(Bundle savedInstanceState) {
 super.onCreate(savedInstanceState);
 setContentView(R.layout.activity_main);
 GuaGuaLe guaguaLeView = (GuaGuaLe) findViewById(R.id.guaguale);
 //注册了一个观察者
 guaguaLeView.setmOnCompleteListener(new GuaGuaLe.OnCompleteListener() {
  @Override
  public void complete() {
  Toast.makeText(MainActivity.this, "用户绘制了大部分", Toast.LENGTH_SHORT).show();
  }
 });
 guaguaLeView.setTextByUser("Android 新技能Get");
 
 }
}
 
 /**中奖文本变量*/
 private String mText;//显示文本
 private Paint mTextPaint;//绘制文本的画笔
 private Rect mTextBounds;//文本的边界
 //以下两个是新增的
 private int mTextSize;//文本大小
 private int mTextColor;//文本颜色
 
 //对外设置调用的设置文本公开方法
 public void setTextByUser(String text){
 mText = text;
 mTextPaint.getTextBounds(mText,0,mText.length(),mTextBounds);//重新计算文本的范围
 }
 
在构造函数中定义
 public GuaGuaLe(Context context, AttributeSet attrs) {
 super(context, attrs);
 //获取自定义属性的值
 TypedArray a = context.obtainStyledAttributes(attrs,R.styleable.GuaGuaLe);
 mText = a.getString(R.styleable.GuaGuaLe_text);
 mTextColor = a.getColor(R.styleable.GuaGuaLe_textColor, Color.parseColor("#c0c0c0"));
 //TypedValue.applyDimension内部就是将sp,dp转化为px
 mTextSize = a.getDimensionPixelSize(R.styleable.GuaGuaLe_textSize, (int) TypedValue.applyDimension
  (TypedValue.COMPLEX_UNIT_SP,22,getResources().getDisplayMetrics()));
 a.recycle();
 initView();
 }

如下图:

Implementierungsprinzip und Codeerklärung für Android-Rubbelkarten

下图是用户自行设置的文本

Implementierungsprinzip und Codeerklärung für Android-Rubbelkarten

经过上面几个步骤,就可以完成呱呱卡的实现效果了。

总结一下: 
(1)圆形圆角图片的原理 
1.绘制图片 
2.paint.setXfermode(DstIn); – 取交集以先绘制的图层为主 Dst是背景图片 
3.绘制圆形图片

1.绘制圆形图片 
2.paint.setXfermode(SrcIn); – 取交集以后绘制的图层为主 Src是结构图片 
3.绘制图片

(2)双缓冲技术绘图【在内存中绘制,再一次性位图拷贝到屏幕上】 
1.在内存中创建与画布一致宽高的canvas 
2.在缓冲区画图 
3.将缓冲区位图拷贝到当前画布上 
4.释放缓冲区 
自己的理解是:在内存中定义一个画板,然后在上面绘图,将绘图结果放在一个Bitmap中,最后让系统的Canvas绘制这个Bitmap

(3)Bimtap获取像素数据(方式一) 
bm.getPixels(x,y) 直接获取x,y坐标处的像素点数据

(4)获取绘制文本的长宽的(方式一) 
1.计算文字所在的矩形,获取宽高[Rect] 
Rect rect = new Rect(); 
paint.getTextBounds(str,0,str,length,rect); 
int w = rect.width; 
int h = rect.height;

(5)自定义View 
1.在values目录下新建attrs.xml文件 
2.设置属性有text,textColor,textSize 
3.在布局文件中引用 
4.在构造函数中获取自定义属性 
5.对外提供方法,让用户动态的设置中奖信息

(6)获取屏幕密度(方式一) 
DisplayMetrics dm = getResource().getDisplayMetrics(); 
float density = dm.density; – 像素比例(0.75/1.0/1.5/2.0/3.0/4.0)显示的就是px/dp的比例 
int densityDpi = dm.densityDpi; – 每寸像素(120/160/240/320/480/640) 
int screenWidth = dm.widthPixels; – 宽像素 
int screenHeight = dm.heightPixels; – 高像素

(7) Einige Methoden von Canva
drawColor zeichnet die Hintergrundfarbe
drawText zeichnet den Text
drawBitmap zeichnet das Bild
[drawBitmap(bitmap,x,y,paint) – zeichnet das Bild zu den angegebenen Koordinaten]
[drawBitma(bitmap,srcRect,dstRect,paint) – Geben Sie den Bereich des Bildes an, dessen Parameter die Koordinaten der vier Eckpunkte oben links, unten rechts sind]
drawRoundRect zeichnet a abgerundetes Rechteck
[drawRoundRect(rectf,rx, ry,paint) – rx (Eckenradius in x-Richtung) ry (Eckenradius in y-Richtung)]

(8) Einige allgemeine Attribute von Paint
setColor/setARGB legt die Zeichnungsfarbe fest
setAlpha legt die Zeichnungstransparenz fest
setAntiAlias ​​​​legt die Glättung fest
setDither legt die Bildditherung fest
setStyle legt den Stil des Pinsels fest
setStrokeWidth legt die Breite fest des Pinsels
setXfermode legt die Verarbeitungsmethode fest, wenn sich Grafiken überlappen
setStrokeJoin-Einstellung Grafikkombinationsmethoden
setTextSize legt die Größe des Textes fest,

(9) Pfad (verbinden Sie N Punkte in der Ansicht in einen „Pfad“ und ruft dann den drawPath von Canvas auf, um Grafiken zu zeichnen.)
Freien Pfad zurücksetzen
moveTo (x, y) Verschieben Sie den Startpunkt zum x-, y-Koordinatenpunkt
lineZeichnen Sie eine gerade Linie
QuadUm eine Kurve zu zeichnen

Das Obige ist der gesamte Inhalt dieses Artikels. Sie haben den Preis gewonnen. Schon? Ich hoffe, dass es für das Studium aller hilfreich sein wird.

Weitere Artikel zu Android-Rubbelkarten-Implementierungsprinzipien und Code-Erklärungen finden Sie auf der chinesischen PHP-Website!

Stellungnahme:
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn