GVKun编程网logo

Android自定义View实现箭头沿圆转动实例代码(android箭头指示)

32

关于Android自定义View实现箭头沿圆转动实例代码和android箭头指示的问题就给大家分享到这里,感谢你花时间阅读本站内容,更多关于Android自定义view实现进度条加载效果实例代码、An

关于Android自定义View实现箭头沿圆转动实例代码android箭头指示的问题就给大家分享到这里,感谢你花时间阅读本站内容,更多关于Android 自定义view实现进度条加载效果实例代码、Android自定义View之旅(二)继承View实现自定义、Android自定义View实现五子棋游戏、Android自定义view实现倒计时控件等相关知识的信息别忘了在本站进行查找喔。

本文目录一览:

Android自定义View实现箭头沿圆转动实例代码(android箭头指示)

Android自定义View实现箭头沿圆转动实例代码(android箭头指示)

具体代码如下所示:

//MyCircleView类
public class MyCircleView extends View{
 //当前画笔画圆的颜色
 private int CurrenCircleBoundColor;
 private Paint paint;
 ////从xml中获取的颜色
 private int circleBundColor;
 private float circleBoundWidth;
 private float pivotX;
 private float pivotY;
 private float radius=130;
 private float currentDegree=0;
 private int currentSpeed=1;
 private boolean isPause=false;
 public MyCircleView(Context context) {
  super(context);
  initView(context);
 }
 public MyCircleView(Context context,@Nullable AttributeSet attrs) {
  super(context,attrs);
  initView(context);
  TypedArray typedArray = context.obtainStyledAttributes(attrs,R.styleable.MyCircleView);
  for (int i = 0; i < typedArray.getIndexCount(); i++) {
   //就是我们自定义的属性的资源id
   int attr = typedArray.getIndex(i);
   switch (attr){
    case R.styleable.MyCircleView_circlr_bound_color:
     circleBundColor = typedArray.getColor(attr,Color.RED);
     CurrenCircleBoundColor=circleBundColor;
     break;
    case R.styleable.MyCircleView_circlr_bound_width:
     circleBoundWidth = typedArray.getDimension(attr,3);
     break;
   }
  }
 }
 public MyCircleView(Context context,@Nullable AttributeSet attrs,int defStyleAttr) {
  super(context,attrs,defStyleAttr);
  initView(context);
 }
 private void initView(Context context){
  paint = new Paint();
 }
 public void setColor(int color){
  if (CurrenCircleBoundColor!=color){
   CurrenCircleBoundColor=color;
  }else {
   CurrenCircleBoundColor=circleBundColor;
  }
 }
 @Override
 protected void onDraw(Canvas canvas) {
  super.onDraw(canvas);
  paint.setAntiAlias(true);
  paint.setColor(CurrenCircleBoundColor);
  paint.setstrokeWidth(circleBoundWidth);
  paint.setStyle(Paint.Style.stroke);
  pivotX = getWidth() / 2;
  pivotY = getHeight() / 2;
  canvas.drawCircle(pivotX,pivotY,radius,paint);
  canvas.save();
  //旋转画布,如果旋转的的度数大的话,视觉上看着是旋转快的
  canvas.rotate(currentDegree,pivotX,pivotY);
  //提供了一些api可以用来画线(画路径)
  Path path = new Path();
  //从哪开始画 从A开始画
  path.moveto(pivotX+radius,pivotY);
  //从A点画一个直线到D点
  path.lineto(pivotX+radius-20,pivotY-20);
  //从D点画一个直线到B点
  path.lineto(pivotX+radius,pivotY+20);
  //从B点画一个直线到C点
  path.lineto(pivotX+radius+20,pivotY-20);
  //闭合 -- 从C点画一个直线到A点
  path.close();
  paint.setStyle(Paint.Style.FILL);
  paint.setColor(Color.BLACK);
  canvas.drawPath(path,paint);
  canvas.restore();
  //旋转的度数一个一个度数增加,如果乘以一个速度的话,按一个速度速度增加
  currentDegree+=1*currentSpeed;
  if (!isPause){
   invalidate();
  }
 }
 public void speed(){
  ++currentSpeed;
  if (currentSpeed>=10){
   currentSpeed=10;
   Toast.makeText(getContext(),"我比闪电还快",Toast.LENGTH_SHORT).show();
  }
 }
 public void slowDown(){
  --currentSpeed;
  if (currentSpeed<=1){
   currentSpeed=1;
  }
 }
 public void pauSEOrStart(){
  //如果是开始状态的话去重新绘制
  if (isPause){
   isPause=!isPause;
   invalidate();
  }else {
   isPause=!isPause;
  }
 }
}
//主页面
public class MainActivity extends AppCompatActivity {
 //全局变量
 private MyCircleView my_view;
 @Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_main);
  //找控件
  my_view = (MyCircleView) findViewById(R.id.my_view);
 }
 public void onClick(View view){
  my_view.setColor(Color.BLUE);
 }
 public void add(View view){
  my_view.speed();
 }
 public void slow(View view){
  my_view.slowDown();
 }
 public void pauSEOrStart(View view){
  my_view.pauSEOrStart();
 }
}
主页面布局
<RelativeLayout
 xmlns:android="http://schemas.android.com/apk/res/android"
 xmlns:tools="http://schemas.android.com/tools"
 xmlns:app="http://schemas.android.com/apk/res-auto"
 android:layout_width="match_parent"
 android:layout_height="match_parent"
 tools:context="com.example.lx_20170928.MainActivity">
 <Button
  android:id="@+id/set_color_btn"
  android:layout_width="match_parent"
  android:layout_height="wrap_content"
  android:layout_centerHorizontal="true"
  android:onClick="onClick"
  android:text="设置颜色" />
 <Button
  android:id="@+id/add"
  android:layout_width="match_parent"
  android:layout_height="wrap_content"
  android:layout_below="@id/set_color_btn"
  android:layout_centerHorizontal="true"
  android:onClick="add"
  android:text="加速" />
 <Button
  android:id="@+id/slow"
  android:layout_width="match_parent"
  android:layout_height="wrap_content"
  android:layout_below="@+id/add"
  android:layout_centerHorizontal="true"
  android:onClick="slow"
  android:text="减速" />
 <Button
  android:id="@+id/pause_or_start"
  android:layout_width="match_parent"
  android:layout_height="wrap_content"
  android:layout_below="@+id/slow"
  android:layout_centerHorizontal="true"
  android:onClick="pauSEOrStart"
  android:text="暂定/开始" />
  <com.example.lx_20170928.MyCircleView
   android:layout_width="wrap_content"
   android:layout_height="wrap_content"
   android:id="@+id/my_view"
   android:layout_centerInParent="true"
   app:circlr_bound_color="@color/colorAccent"
   app:circlr_bound_width="3dp"
   />
</RelativeLayout>
//在values建一个attrs.xml
<resources>
 <declare-styleable name="MyCustomCircleArrowView">
  <attr name="circlr_bound_width" format="dimension"></attr>
  <attr name="circlr_bound_color" format="color"></attr>
 </declare-styleable>
</resources>

效果图如下所示:

总结

以上所述是小编给大家介绍的Android自定义view实现箭头沿圆转动实例代码,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对编程小技巧网站的支持!

Android 自定义view实现进度条加载效果实例代码

Android 自定义view实现进度条加载效果实例代码

  这个其实很简单,思路是这样的,就是拿view的宽度,除以点的点的宽度+二个点 之间的间距,就可以算出大概能画出几个点出来,然后就通过canvas画出点,再然后就是每隔多少时间把上面移动的点不断的去改变它的坐标就可以,

效果如下:

分析图:

代码如下:

package com.example.dotloadview;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.os.Handler;
import android.util.AttributeSet;
import android.view.View;
/**
 * Created by zhouguizhi on 6/18/15.
 */
public class DotLoadProgressView extends View {
  private boolean isstart = false;
  private int dotIndex =0;//点下面的角标
  private int dotNumber = 0;
  private Paint mPaint;
  private int paddingTop = 50;
  private int paddingBottom = 50;
  private int paddingLeft = 30;
  private int paddingRight = 30;
  private Handler mHandler = new Handler();
  private long DELAY_TIME = 300;
  private Bitmap bitmap;
  private int itemDotSpace = 10;//二个点之间的距离
  private Bitmap moveBitmap ;
  private Runnable mRunnable = new Runnable() {
    @Override
    public void run() {
      if (dotNumber != 0){
        dotIndex = (dotIndex + 1) % dotNumber;
      }
      invalidate();
      mHandler.postDelayed(mRunnable,DELAY_TIME);
    }
  };
  public DotLoadProgressView(Context context,AttributeSet attrs) {
    super(context,attrs);
    moveBitmap = BitmapFactory.decodeResource(getResources(),R.drawable.active_dot);
    init();
  }
  private void init() {
    mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
    mPaint.setStyle(Paint.Style.FILL);
    mPaint.setDither(true);
    mPaint.setAntiAlias(true);
  }
  @Override
  protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    for(int i=0;i<dotNumber;i++){
      canvas.drawBitmap(bitmap,paddingLeft+i*itemDotSpace+bitmap.getWidth()*i,paddingTop,mPaint);
    }
    if(isstart){
      canvas.drawBitmap(moveBitmap,paddingLeft+dotIndex*itemDotSpace+moveBitmap.getWidth()*dotIndex,mPaint);
    }
  }
  @Override
  protected void onMeasure(int widthMeasureSpec,int heightMeasureSpec) {
    int parentWidth = MeasureSpec.getSize(widthMeasureSpec);
    bitmap = BitmapFactory.decodeResource(getResources(),R.drawable.inactive_dot);
    setMeasuredDimension(parentWidth,bitmap.getHeight()+paddingTop+paddingBottom);
    int widthWithoutPadding = parentWidth - paddingLeft - paddingRight;
    dotNumber = calculateDotsNumber(widthWithoutPadding);
  }
  private int calculateDotsNumber(int width) {
    int number = (width / (itemDotSpace + bitmap.getWidth()));
    return number;
  }
  public void startProgress() {
    isstart = true;
    dotIndex = -1;
    mHandler.removeCallbacks(mRunnable);
    mHandler.post(mRunnable);
  }
  public void stopProgress() {
    isstart = false;
    mHandler.removeCallbacks(mRunnable);
    invalidate();
  }
  @Override
  protected void onAttachedToWindow() {
    super.onAttachedToWindow();
    startProgress();
  }
  @Override
  protected void onDetachedFromWindow() {
    super.onDetachedFromWindow();
    stopProgress();
  }
}

布局代码:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
  xmlns:android="http://schemas.android.com/apk/res/android"
  android:layout_width="match_parent"
  android:layout_height="wrap_content">
  <com.example.dotloadview.DotLoadProgressView
    android:id="@+id/progress1"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    />
</RelativeLayout>

二个点其实是图片,在网上别人项目中拿出来用的,如果感觉那个点图片大了的话,有二个办法,第一叫你们美工切小点,第二就是通过drawable.setBound()去限制他大小了,我在这其实是偷懒,没有那么做,当然这点你也可以通过shade去做,而不用图片的话
最终的效果图还是这样的:

总结

以上所述是小编给大家介绍的Android 自定义view实现进度条加载效果实例代码,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对编程小技巧网站的支持!

Android自定义View之旅(二)继承View实现自定义

Android自定义View之旅(二)继承View实现自定义

继承View是实现自定义view的重要方式,通过自定义属性以实现xml中的便捷使用,通过重写onMeasure和onDraw方法自定义view的绘制过程,通过拦截事件响应完成特定的行为,让想法变为现实。

上一篇文章Android自定义View之旅(一)自定义View的几种方式

本文将通过实战来讲述如何通过继承View实现自定义,先仔细看看需求,一个声音波形控件,持续动画效果,单靠上面的简单实现是不可能的了,需要的效果如下:

在这里插入图片描述

1、继承View实现自定义view

在上一篇文章中,我们详细介绍了简单的“自定义”如何实现,继承了安卓系统的原生控件,再在此基础上完成附加的功能样式或者更改原有的功能样式,可以迅速达到想要的效果,缺点是会受到父类的限制、无法完成复杂的需求。

对于这次需要实现的声音波形控件,我们需要新建一个类VoiceLineView,继承自View

/**
 * 自定义声音振动曲线view
 */
public class VoiceLineView extends View {

    public VoiceLineView(Context context) {
        super(context);
    }

    public VoiceLineView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public VoiceLineView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

}

2、提供一些自定义的属性

通过自定义属性以实现xml中的便捷使用,就像我们在使用TextView的时候,设置android:text="Hello World!"

首先,需要在src/main/res/values文件夹下的attrs.xml文件(如不存在此文件新建一个即可)中新增如下代码:

<?xml version="1.0" encoding="utf-8"?>
<resources>
	······
	<!--name为获取属性时使用,不可与其他控件的自定义属性重复-->
    <declare-styleable name="voiceView">
        <!--中间线的颜色,就是波形的时候,大家可以看到,中间有一条直线,就是那个-->
        <attr name="middleLine" format="color" />
        <!--中间线的高度,中间线的宽度是充满的无需设定-->
        <attr name="middleLineHeight" format="dimension" />
        <!--波动的线的颜色-->
        <attr name="voiceLine" format="color" />
        <!--波动线的横向移动速度,线的速度的反比,即这个值越小,线横向移动越快,越大线移动越慢,默认90-->
        <attr name="linespeed" format="integer" />
        <!--所输入音量的最大值,默认是100-->
        <attr name="maxVolume" format="float" />
        <!--灵敏度,默认值是4-->
        <attr name="sensibility">
            <enum name="one" value="1" />
            <enum name="two" value="2" />
            <enum name="three" value="3" />
            <enum name="four" value="4" />
            <enum name="five" value="5" />
        </attr>
        <!--精细度,绘制曲线的时候,每几个像素绘制一次,默认是1,一般,这个值越小,曲线越顺滑,但在一些旧手机上,会出现帧率过低的情况,可以把这个值调大一点,在图片的顺滑度与帧率之间做一个取舍-->
        <attr name="fineness">
            <enum name="one" value="1" />
            <enum name="two" value="2" />
            <enum name="three" value="3" />
        </attr>
    </declare-styleable>
    ······
</resources>

新增一个declare-styleable标签声明一个属性集,根据需要,在declare-styleable标签中增加多个attr标签声明属性,attr标签根据控件的可设置属性进行配置。关于attr自定义属性的类型可以看文章Android中attr属性的类型。

随后在VoiceLineView类中修改代码如下:

public class VoiceLineView extends View {

    private int middleLineColor = Color.BLACK;
    private int voiceLineColor = Color.BLACK;
    private float middleLineHeight = 4;
    private int sensibility = 4;
    private float maxVolume = 100;
    private int fineness = 1;
    private int linespeed = 90;

    public VoiceLineView(Context context) {
        super(context);
    }

    public VoiceLineView(Context context, AttributeSet attrs) {
        super(context, attrs);
        initAtts(context, attrs);
    }

    public VoiceLineView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        initAtts(context, attrs);
    }

    private void initAtts(Context context, AttributeSet attrs) {
        TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.voiceView);
        voiceLineColor = typedArray.getColor(R.styleable.voiceView_voiceLine, Color.BLACK);
        maxVolume = typedArray.getFloat(R.styleable.voiceView_maxVolume, 100);
        sensibility = typedArray.getInt(R.styleable.voiceView_sensibility, 4);
        middleLineColor = typedArray.getColor(R.styleable.voiceView_middleLine, Color.BLACK);
        middleLineHeight = typedArray.getDimension(R.styleable.voiceView_middleLineHeight, 4);
        linespeed = typedArray.getInt(R.styleable.voiceView_linespeed, 90);
        fineness = typedArray.getInt(R.styleable.voiceView_fineness, 1);
        typedArray.recycle();
    }

}

在布局中使用如下:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <VoiceLineView
        android:id="@+id/voice_line"
        android:layout_width="match_parent"
        android:layout_height="200dp"
        app:middleLine="@color/colorPrimary"
        app:middleLineHeight="1dp"
        app:voiceLine="@color/colorPrimary"
        app:linespeed="90"
        app:maxVolume="100"
        app:sensibility="four"
        app:fineness="one" />

</LinearLayout>

3、重写onDraw方法,持续绘制声波

话不多说,直接上代码:

public class VoiceLineView extends View {
	······

    private Paint paint;
    private Paint paintVoicLine;
    private float translateX = 0;
    private boolean isSet = false;
    private float amplitude = 1;
    private float volume = 10;
    private long lastTime = 0;
    private int linespeed = 90;
    List<Path> paths = null;

	······

    public void setVolume(int volume) {
        if (volume > maxVolume * sensibility / 25) {
            isSet = true;
            this.targetVolume = getHeight() * volume / 2 / maxVolume;
        }
    }

    @Override
    protected void onDraw(Canvas canvas) {
        drawMiddleLine(canvas);
        drawVoiceLine(canvas);
        run();
    }

    private void drawMiddleLine(Canvas canvas) {
        if (paint == null) {
            paint = new Paint();
            paint.setColor(middleLineColor);
            paint.setAntiAlias(true);
        }
        canvas.save();
        canvas.drawRect(0, getHeight() / 2 - middleLineHeight / 2, getWidth(), getHeight() / 2 + middleLineHeight / 2, paint);
        canvas.restore();
    }

    private void drawVoiceLine(Canvas canvas) {
        lineChange();
        if (paintVoicLine == null) {
            paintVoicLine = new Paint();
            paintVoicLine.setColor(voiceLineColor);
            paintVoicLine.setAntiAlias(true);
            paintVoicLine.setStyle(Paint.Style.stroke);
            paintVoicLine.setstrokeWidth(2);
        }
        canvas.save();
        if (paths == null) {
            paths = new ArrayList<>(20);
            for (int i = 0; i < 20; i++) {
                paths.add(new Path());
            }
        }
        int moveY = getHeight() / 2;
        for (int i = 0; i < paths.size(); i++) {
            paths.get(i).reset();
            paths.get(i).moveto(getWidth(), getHeight() / 2);
        }
        for (float i = getWidth() - 1; i >= 0; i -= fineness) {
            amplitude = 4 * volume * i / getWidth() - 4 * volume * i * i / getWidth() / getWidth();
            for (int n = 1; n <= paths.size(); n++) {
                float sin = amplitude * (float) Math.sin((i - Math.pow(1.22, n)) * Math.PI / 180 - translateX);
                paths.get(n - 1).lineto(i, (2 * n * sin / paths.size() - 15 * sin / paths.size() + moveY));
            }
        }
        for (int n = 0; n < paths.size(); n++) {
            if (n == paths.size() - 1) {
                paintVoicLine.setAlpha(255);
            } else {
                paintVoicLine.setAlpha(n * 130 / paths.size());
            }
            if (paintVoicLine.getAlpha() > 0) {
                canvas.drawPath(paths.get(n), paintVoicLine);
            }
        }
        canvas.restore();
    }

    private void lineChange() {
        if (lastTime == 0) {
            lastTime = System.currentTimeMillis();
            translateX += 1.5;
        } else {
            if (System.currentTimeMillis() - lastTime > linespeed) {
                lastTime = System.currentTimeMillis();
                translateX += 1.5;
            } else {
                return;
            }
        }
        if (volume < targetVolume && isSet) {
            volume += getHeight() / 30;
        } else {
            isSet = false;
            if (volume <= 10) {
                volume = 10;
            } else {
                if (volume < getHeight() / 30) {
                    volume -= getHeight() / 60;
                } else {
                    volume -= getHeight() / 30;
                }
            }
        }
    }

    public void run() {
        invalidate();
    }

}

到此,需求就已经完成啦,使用时按第2点加入布局,代码中使用:

VoiceLineView voiceLine = findViewById(R.id.voice_line);

// 循环设置音量即可绘制声波图
voiceLine.setVolume(volume);

Android自定义View实现五子棋游戏

Android自定义View实现五子棋游戏

本文实例为大家分享了Android实现五子棋游戏的具体代码,供大家参考,具体内容如下

直接上效果图

原理

从棋盘到棋子,到开始下棋的各类点击事件,均在 ChessView 中实现,这个 View 没有提供自定义属性(因为我觉得没有必要~~~)。

项目GitHub地址:Wuziqi

实现步骤

1.新建一个棋子类,这个类非常简单,代码如下:

public class Chess {

 public enum Color {BLACK, WHITE, NONE}
 private Color color;

 public Chess(){
  this.color = Color.NONE;
 }
 public Color getColor() {
  return color;
 }
 public void setColor(Color color) {
  this.color = color;
 }
}

每个棋子类有三种状态,即 WHITE,BLACK,NONE。这里我们使用枚举来表示这三种状态。

2. 自定义 ChessView 类,这个类就是核心类了,我们这个五子棋的所有逻辑都是在这个类里面实现。构造方法初始化各个字段,代码如下:

public ChessView(Context context, AttributeSet attrs, int defStyleAttr) {
  super(context, attrs, defStyleAttr);
  // 初始化字段 mEveryPlay,悔棋会用到
  initEveryPlay();
  // 初始化每个棋子,设置属性为 NONE
  initChess();
  // 初始化棋盘画笔
  initBoardPaint();
  // 初始化棋子画笔
  initChessPaint();
  // 初始化背景画笔
  initBgPaint();
 }

各个方法的具体实现如下:

private void initEveryPlay() {
  // 初始化 List 大小,此方法不影响 list.size() 返回值
  mEveryPlay = new ArrayList<>(225);
 }

 private void initChess() {
  mChessArray = new Chess[15][15];
  for (int i = 0; i < mChessArray.length; i++) {
   for (int j = 0; j < mChessArray[i].length; j++) {
    mChessArray[i][j] = new Chess();
   }
  }
 }

 private void initChessPaint() {
  mChessPaint = new Paint();
  mChessPaint.setColor(android.graphics.Color.WHITE);
  mChessPaint.setAntiAlias(true);
 }

 private void initBoardPaint() {
  mBoardPaint = new Paint();
  mBoardPaint.setColor(android.graphics.Color.BLACK);
  mBoardPaint.setStrokeWidth(2);
 }

 private void initBgPaint() {
  mBgPaint = new Paint();
  mBgPaint.setColor(android.graphics.Color.GRAY);
  mBgPaint.setAntiAlias(true);
 }

3. 重写 onMeasure() 方法,强制将 View 大小变为正方形,代码如下:

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {

  int widthSize = MeasureSpec.getSize(widthMeasureSpec);
  int heightSize = MeasureSpec.getSize(heightMeasureSpec);

  int min = widthSize < heightSize ? widthSize : heightSize;
  // 五子棋标准棋盘线条数目为 15 x 15,为了后面计算坐标方便,我们将 View 的宽高处理为 16 的整数倍
  min = min / 16 * 16;

  setMeasuredDimension(min, min);
 }

之所以设置为 16 的整数倍而不是 15,是因为如果设置成 15,那么棋盘的背景就会跟棋盘最边界的线条重合,此时如果有棋子落在边界,棋子将不能显示完全。

4. 重点来了,重写 onDraw() 方法,绘制出棋盘,代码如下:

@Override
protected void onDraw(Canvas canvas) {
  int height = getMeasuredHeight();
  int width = getMeasuredWidth();
  int avg = height / 16;

  canvas.drawRect(0, 0, width, height, mBgPaint);
  for (int i = 1; i < 16; i++) {
   // 画竖线
   canvas.drawLine(avg * i, avg, avg * i, height - avg, mBoardPaint);
   // 画横线
   canvas.drawLine(avg, avg * i, width - avg, avg * i, mBoardPaint);
  }
  for (int i = 1; i < 16; i++) {
   for (int j = 1; j < 16; j++) {
    switch (mChessArray[i - 1][j - 1].getColor()) {
     case BLACK:
      mChessPaint.setColor(android.graphics.Color.BLACK);
      break;
     case WHITE:
      mChessPaint.setColor(android.graphics.Color.WHITE);
      break;
     case NONE:
      continue;
    }
    canvas.drawCircle(avg * i, avg * j, avg / 2 - 0.5f, mChessPaint);
   }
  }
 }

这样我们就将整个棋盘画出来了,之后我们只需要改变数组 mChessArray[][] 里面对象的 Color 属性,再调用 invalidate() 方法便可以刷新棋盘了。

5. 接下来,我们便要处理点击事件,实现对弈的逻辑了,重写 onTouchEvent() 方法,代码如下:

@Override
public boolean onTouchEvent(MotionEvent event) {
  switch (event.getAction()) {
   case MotionEvent.ACTION_DOWN:
    // 如果棋盘被锁定(即胜负已分,返回查看棋局的时候)
    // 此时只允许查看,不允许落子了
    if (isLocked) {
     return true;
    }
    float x = event.getX();
    float y = event.getY();
    // 以点击的位置为中心,新建一个小矩形
    Rect rect = getLittleRect(x, y);
    // 获得上述矩形包含的棋盘上的点
    Point point = getContainPoint(rect);
    if (point != null) {
     // 若点不为空,则刷新对应位置棋子的属性
     setChessState(point);
     // 记录下每步操作,方便悔棋操作
     mEveryPlay.add(point);
     if (gameIsOver(point.x, point.y)) {
      // 游戏结束弹窗提示
      showDialog();
     }
     // 更改游戏玩家
     isBlackPlay = !isBlackPlay;
    }
    break;
   case MotionEvent.ACTION_MOVE:
    break;
   case MotionEvent.ACTION_UP:
    break;
  }
  return super.onTouchEvent(event);
 }

下面分别来说说调用到的各个方法的实现思路:

getLittleRect() 

/**
  * 以传入点为中心,获得一个矩形
  *
  * @param x 传入点 x 坐标
  * @param y 传入点 y 坐标
  * @return 所得矩形
  */
 private Rect getLittleRect(float x, float y) {
  int side = getMeasuredHeight() / 16;
  int left = (int) (x - side / 2);
  int top = (int) (y - side / 2);
  int right = (int) (x + side / 2);
  int bottom = (int) (y + side / 2);
  return new Rect(left, top, right, bottom);
 }

getContainPoint()

 /**
  * 获取包含在 rect 中并且是能够下棋的位置的点
  *
  * @param rect 矩形
  * @return 返回包含的点,若没有包含任何点或者包含点已有棋子返回 null
  */
 private Point getContainPoint(Rect rect) {
  int avg = getMeasuredHeight() / 16;
  for (int i = 1; i < 16; i++) {
   for (int j = 1; j < 16; j++) {
    if (rect.contains(avg * i, avg * j)) {
     Point point = new Point(i - 1, j - 1);
     // 包含点没有棋子才返回 point
     if (mChessArray[point.x][point.y].getColor() == Chess.Color.NONE) {
      return point;
     }
     break;
    }
   }
  }
  return null;
 }

showDialog()

顺便一提,这个方法用的是 v7 包里面的对话框,因为这样可以在版本较低的安卓平台下也可以得到不错的显示效果,效果如下:

/**
  * 游戏结束,显示对话框
  */
 private void showDialog() {
  AlertDialog.Builder builder = new AlertDialog.Builder(getContext());
  builder.setTitle("游戏结束");
  if (isBlackPlay) {
   builder.setMessage("黑方获胜!!!");
  } else {
   builder.setMessage("白方获胜!!!");
  }
  builder.setCancelable(false);
  builder.setPositiveButton("重新开始", new DialogInterface.OnClickListener() {
   @Override
   public void onClick(DialogInterface dialog, int which) {
    resetChessBoard();
    dialog.dismiss();
   }
  });
  builder.setNegativeButton("返回查看", new DialogInterface.OnClickListener() {
   @Override
   public void onClick(DialogInterface dialog, int which) {
    isLocked = true;
    dialog.dismiss();
   }
  });
  builder.show();
 }

setChessState()

/**
  * 重新设定用户所点位置的棋子状态
  *
  * @param point 棋子的位置
  */
 private void setChessState(Point point) {
  if (isBlackPlay) {
   mChessArray[point.x][point.y].setColor(Chess.Color.BLACK);
  } else {
   mChessArray[point.x][point.y].setColor(Chess.Color.WHITE);
  }
  invalidate();
 }

以上几个方法都较为简单不多说了,接下来重点讲一下判断游戏结束的逻辑。

- gameIsOver ()

 /**
  * 判断游戏是否结束,游戏结束标志:当前落子位置与其他同色棋子连成 5 个
  *
  * @param x 落子位置 x 坐标
  * @param y 落子位置 y 坐标
  * @return 若连成 5 个,游戏结束,返回 true,负责返回 false
  */
 private boolean gameIsOver(int x, int y) {
  Chess.Color color = mChessArray[x][y].getColor();
  return isOverA(x, y, color) || isOverB(x, y, color) || isOverC(x, y, color) || isOverD(x, y, color);
 }

这个方法用来判断游戏是否结束,思路便是以当前落子位置为基准,去寻找竖直、水平、左上至右下、左下至右上四个方向是否连成 5 子,分别对应 isOverA(), isOverB(), isOverC(), isOverD() 四个方法,这四个方法的实现如下:

private boolean isOverA(int x, int y, Chess.Color color) {
  int amount = 0;
  for (int i = y; i >= 0; i--) {
   if (mChessArray[x][i].getColor() == color) {
    amount++;
   } else {
    break;
   }
  }
  for (int i = y; i < mChessArray[x].length; i++) {
   if (mChessArray[x][i].getColor() == color) {
    amount++;
   } else {
    break;
   }
  }
  // 循环执行完成后,当前落子位置算了两次,故条件应是大于 5
  return amount > 5;
 }

 private boolean isOverB(int x, int y, Chess.Color color) {
  int amount = 0;
  for (int i = x; i >= 0; i--) {
   if (mChessArray[i][y].getColor() == color) {
    amount++;
   } else {
    break;
   }
  }
  for (int i = x; i < mChessArray.length; i++) {
   if (mChessArray[i][y].getColor() == color) {
    amount++;
   } else {
    break;
   }
  }
  // 循环执行完成后,当前落子位置算了两次,故条件应是大于 5
  return amount > 5;
 }

 private boolean isOverC(int x, int y, Chess.Color color) {
  int amount = 0;
  for (int i = x, j = y; i >= 0 && j >= 0; i--, j--) {
   if (mChessArray[i][j].getColor() == color) {
    amount++;
   } else {
    break;
   }
  }
  for (int i = x, j = y; i < mChessArray.length && j < mChessArray[i].length; i++, j++) {
   if (mChessArray[i][j].getColor() == color) {
    amount++;
   } else {
    break;
   }
  }
  // 循环执行完成后,当前落子位置算了两次,故条件应是大于 5
  return amount > 5;
 }
 private boolean isOverD(int x, int y, Chess.Color color) {
  int amount = 0;
  for (int i = x, j = y; i < mChessArray.length && j >= 0; i++, j--) {
   if (mChessArray[i][j].getColor() == color) {
    amount++;
   } else {
    break;
   }
  }
  for (int i = x, j = y; i >= 0 && j < mChessArray[i].length; i--, j++) {
   if (mChessArray[i][j].getColor() == color) {
    amount++;
   } else {
    break;
   }
  }
  // 循环执行完成后,当前落子位置算了两次,故条件应是大于 5
  return amount > 5;
 }

6. 最后定义两个公有方法,方便 Activity 调用,用来执行悔棋和重置棋盘操作。两个方法代码如下:

/**
  * 悔棋,实现思路为:记录每一步走棋的坐标,若点击了悔棋,
  * 则拿出最后记录的坐标,对 mChessArray 里面对应坐标的
  * 棋子进行处理(设置颜色为 NONE),并移除集合里面最后
  * 一个元素
  */
 public void retract() {
  if (mEveryPlay.isEmpty()) {
   return;
  }
  Point point = mEveryPlay.get(mEveryPlay.size() - 1);
  mChessArray[point.x][point.y].setColor(Chess.Color.NONE);
  mEveryPlay.remove(mEveryPlay.size() - 1);
  isLocked = false;
  isBlackPlay = !isBlackPlay;
  invalidate();
 }

 /**
  * 重置棋盘
  */
 public void resetChessBoard() {
  for (Chess[] chessRow : mChessArray) {
   for (Chess chess : chessRow) {
    chess.setColor(Chess.Color.NONE);
   }
  }
  mEveryPlay.clear();
  isBlackPlay = true;
  isLocked = false;
  invalidate();
 }

到此, ChessView 已经写完了,接下来只要在布局文件里面声明即可。

7. 在 activity_main 布局文件如下,非常简单,我相信不用多说都能看懂:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
 xmlns:android="http://schemas.android.com/apk/res/android"
 xmlns:tools="http://schemas.android.com/tools"
 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.yangqi.wuziqi.ChessView
  android:id="@+id/chessView"
  android:layout_width="match_parent"
  android:layout_height="match_parent"
  android:layout_centerHorizontal="true"/>

 <LinearLayout
  android:layout_width="match_parent"
  android:layout_height="match_parent"
  android:layout_below="@id/chessView"
  android:layout_margin="20dp"
  android:gravity="center_horizontal"
  android:orientation="horizontal">

  <Button
   android:id="@+id/bt_retract"
   android:layout_width="wrap_content"
   android:layout_height="wrap_content"
   android:text="悔棋" />

  <Button
   android:id="@+id/bt_reset"
   android:layout_width="wrap_content"
   android:layout_height="wrap_content"
   android:text="重新开始"/>
 </LinearLayout>
</RelativeLayout>

8. 最后一步了,只需要在 MainActivity 里面拿到 ChessView 对象和两个 Button 按钮,即可实现悔棋与重新开始:

package com.yangqi.wuziqi;

import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.Button;


public class MainActivity extends AppCompatActivity {

 private Button bt_reset;
 private Button bt_retract;
 private ChessView chessView;

 @Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_main);

  initUI();
  initListener();
 }

 private void initListener() {
  bt_reset.setOnClickListener(new View.OnClickListener() {
   @Override
   public void onClick(View v) {
    chessView.resetChessBoard();
   }
  });
  bt_retract.setOnClickListener(new View.OnClickListener() {
   @Override
   public void onClick(View v) {
    chessView.retract();
   }
  });
 }

 private void initUI() {

  bt_reset = (Button) findViewById(R.id.bt_reset);
  bt_retract = (Button) findViewById(R.id.bt_retract);
  chessView = (ChessView) findViewById(R.id.chessView);
 }
}

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。

您可能感兴趣的文章:
  • Android实现五子棋游戏(局域网版)
  • android自定义View实现简单五子棋游戏
  • Android自定义view之围棋动画效果的实现
  • 基于android实现五子棋开发
  • Android自定义View实现五子棋游戏
  • Android自定义View实现五子棋小游戏
  • android简单自定义View实现五子棋
  • Android开发实现的简单五子棋游戏示例
  • Android游戏开发之黑白棋
  • Android实现中国象棋游戏(局域网版)

Android自定义view实现倒计时控件

Android自定义view实现倒计时控件

本文实例为大家分享了Android自定义view实现倒计时控件的具体代码,供大家参考,具体内容如下

直接上代码

自定义TextView

文字展示

public class StrokeTextView extends TextView {

 private TextView borderText = null;///用于描边的TextView
 private Context mContext;

 public StrokeTextView(Context context) {
  super(context);
  mContext = context;
  borderText = new TextView(context);
  init();
 }

 public StrokeTextView(Context context, AttributeSet attrs) {
  super(context, attrs);
  mContext = context;
  borderText = new TextView(context, attrs);
  init();
 }

 public StrokeTextView(Context context, AttributeSet attrs, int defStyle) {
  super(context, attrs, defStyle);
  mContext = context;
  borderText = new TextView(context, attrs, defStyle);
  init();
 }

 public void init() {
  TextPaint tp1 = borderText.getPaint();
  tp1.setStrokeWidth(12);         //设置描边宽度
  tp1.setStyle(Paint.Style.STROKE);        //对文字只描边
  //设置自定义字体
  Typeface fromAsset = Typeface.createFromAsset(mContext.getAssets(), "fonts/Alibaba-PuHuiTi-Heavy.ttf");
  borderText.setTypeface(fromAsset, Typeface.ITALIC); //自定义字体 ITALIC斜体
  borderText.setTextColor(Color.parseColor("#F46059")); //设置描边颜色
  borderText.setShadowLayer(3.0F, 2F, 2F, Color.parseColor("#ffd44042")); //设置阴影效果(投影)
  borderText.setGravity(getGravity());
 }

 @Override
 public void setLayoutParams(ViewGroup.LayoutParams params) {
  super.setLayoutParams(params);
  borderText.setLayoutParams(params);
 }

 @Override
 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
  CharSequence tt = borderText.getText();

  //两个TextView上的文字必须一致
  if (tt == null || !tt.equals(this.getText())) {
   borderText.setText(getText());
   this.postInvalidate();
  }
  super.onMeasure(widthMeasureSpec, heightMeasureSpec);
  borderText.measure(widthMeasureSpec, heightMeasureSpec);
 }

 protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
  super.onLayout(changed, left, top, right, bottom);
  borderText.layout(left, top, right, bottom);
 }

 @Override
 protected void onDraw(Canvas canvas) {
  borderText.draw(canvas);
  super.onDraw(canvas);
 }

}

xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
 xmlns:app="http://schemas.android.com/apk/res-auto"
 xmlns:tools="http://schemas.android.com/tools"
 android:layout_width="match_parent"
 android:background="#F3B243"
 android:layout_height="match_parent"
 tools:context=".countdown.TestCountActivity">

 <RelativeLayout
  android:layout_width="match_parent"
  android:layout_height="wrap_content">
  <com.xiao.test.countdown.StrokeTextView
   android:layout_marginTop="100dp"
   android:id="@+id/tv_test"
   android:layout_width="wrap_content"
   android:layout_height="wrap_content"
   android:background="@drawable/play_advertising_timer_bg"
   android:paddingLeft="15dp"
   android:textColor="#FFFFFF"
   android:textSize="33sp"
   android:layout_centerHorizontal="true"
   android:layout_gravity="center"
   android:gravity="center_vertical"
   android:textandroid:typeface="monospace"
   tools:ignore="RtlSymmetry"
   android:paddingStart="15dp" />

 </RelativeLayout>

</LinearLayout>

倒计时帮助类

public class CountDownHelper {

 private OnCountDownListener onCountDownListener;
 private Disposable disposable;
 private long remainingTime;

 public CountDownHelper(long remainingTime) {
  this.remainingTime = remainingTime;
 }

 /**
  * 回收倒计时
  */
 public void destory() {
  if (disposable != null && !disposable.isDisposed()) {
   disposable.dispose();
  }
 }

 /**
  * 开始倒计时
  */
 public void startCompute() {
  Observable.interval(1, TimeUnit.SECONDS)
    .subscribeOn(Schedulers.newThread())
    .observeOn(AndroidSchedulers.mainThread())
    .subscribe(new Observer<Long>() {
     @Override
     public void onSubscribe(Disposable d) {
      disposable = d;
     }

     @Override
     public void onNext(Long aLong) {
      if (onCountDownListener == null) {
       return;
      }
      remainingTime -= 1000;
      if (remainingTime > 0) {
       int day = (int) (remainingTime / (1000 * 60 * 60 * 24));
       int hour = (int) ((remainingTime % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60));
       int minute = (int) ((remainingTime % (1000 * 60 * 60)) / (1000 * 60));
       int second = (int) ((remainingTime % (1000 * 60)) / 1000);
       String dayStr = day >= 10 ? String.valueOf(day) : "0" + day;
       String hourStr = hour >= 10 ? String.valueOf(hour) : "0" + hour;
       String minuteStr = minute >= 10 ? String.valueOf(minute) : "0" + minute;
       String secondStr = second >= 10 ? String.valueOf(second) : "0" + second;
       onCountDownListener.countDown(dayStr, hourStr, minuteStr, secondStr);
       if (remainingTime <= 0) {
        onCountDownListener.countDownFinish();
        if (disposable != null && !disposable.isDisposed()) {
         disposable.dispose();
        }
       }
      } else {
       onCountDownListener.countDownFinish();
       if (disposable != null && !disposable.isDisposed()) {
        disposable.dispose();
       }
      }
     }

     @Override
     public void onError(Throwable e) {

     }

     @Override
     public void onComplete() {

     }
    });
 }


 /**
  * 设置倒计时回调监听
  *
  * @param onCountDownListener 倒计时监听
  */
 public void setOnCountDownListener(OnCountDownListener onCountDownListener) {
  this.onCountDownListener = onCountDownListener;
 }

 public interface OnCountDownListener {

  /**
   * 倒计时
   *
   * @param day 天
   * @param hour 小时
   * @param minute 分钟
   * @param second 秒
   */
  void countDown(String day, String hour, String minute, String second);

  /**
   * 倒计时完成
   */
  void countDownFinish();
 }
}

TestCountActivity.java

public class TestCountActivity extends AppCompatActivity {
 private CountDownHelper mCountDownHelper;
 private StrokeTextView mTvTest;

 @Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_test_count);

  mTvTest = findViewById(R.id.tv_test);

//设置自定义字体
  Typeface fromAsset = Typeface.createFromAsset(getAssets(), "fonts/Alibaba-PuHuiTi-Heavy.ttf");
  mTvTest.setTypeface(fromAsset, Typeface.ITALIC); //自定义字体 ITALIC斜体

  long aLong = 1787;
  mCountDownHelper = new CountDownHelper(aLong * 1000);
  mCountDownHelper.startCompute();
  mCountDownHelper.setOnCountDownListener(new CountDownHelper.OnCountDownListener() {
   @SuppressLint("SetTextI18n")
   @Override
   public void countDown(String day, String hour, String minute, String second) {
    mTvTest.setText(hour + ":" + minute + ":" + second);
   }

   @Override
   public void countDownFinish() {
    Log.d("", "结束倒计时");
    mCountDownHelper.destory();
    //延时跳转
    new Handler(new Handler.Callback() {
     @Override
     public boolean handleMessage(Message msg) {

      Toast.makeText(TestCountActivity.this, "时间到了", Toast.LENGTH_SHORT).show();

      return false;
     }
    }).sendEmptyMessageDelayed(0, 10000);//表示延迟10秒发送任务

   }
  });


 }
}

引入依赖

implementation ‘io.reactivex.rxjava2:rxjava:2.0.1''
implementation ‘io.reactivex.rxjava2:rxandroid:2.0.1''

欢迎小伙伴们评论

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。

您可能感兴趣的文章:
  • Android实现启动页倒计时效果
  • Android 实现抢购倒计时功能的示例
  • android实现倒计时动态圈
  • android实现圆环倒计时控件
  • android利用handler实现倒计时功能
  • 解决Android-RecyclerView列表倒计时错乱问题
  • Android实现自定义倒计时
  • Android 倒计时控件 CountDownView的实例代码详解
  • Android倒计时神器(CountDownTimer)
  • Android倒计时功能的实现代码
  • Android 简单实现倒计时功能
  • Android自定义TimeButton实现倒计时按钮
  • Android实现倒计时的按钮效果
  • 利用Android设计一个倒计时组件

今天关于Android自定义View实现箭头沿圆转动实例代码android箭头指示的介绍到此结束,谢谢您的阅读,有关Android 自定义view实现进度条加载效果实例代码、Android自定义View之旅(二)继承View实现自定义、Android自定义View实现五子棋游戏、Android自定义view实现倒计时控件等更多相关知识的信息可以在本站进行查询。

本文标签: