返回顶部
首页 > 资讯 > 精选 >怎么在Android中使用ViewDragHelper实现一个拼图游戏
  • 927
分享到

怎么在Android中使用ViewDragHelper实现一个拼图游戏

androidviewdraghelper 2023-05-30 18:05:17 927人浏览 独家记忆
摘要

本篇文章给大家分享的是有关怎么在Android中使用ViewDragHelper实现一个拼图游戏,小编觉得挺实用的,因此分享给大家学习,希望大家阅读完这篇文章后可以有所收获,话不多说,跟着小编一起来看看吧。ViewDragHelper其实V

本篇文章给大家分享的是有关怎么在Android中使用ViewDragHelper实现一个拼图游戏,小编觉得挺实用的,因此分享给大家学习,希望大家阅读完这篇文章后可以有所收获,话不多说,跟着小编一起来看看吧。

ViewDragHelper

其实ViewDragHelper并不是第一个用于分析手势处理的类,gesturedetector也是,但是在和拖动相关的手势分析方面gesturedetector只能说是勉为其难。

关于ViewDragHelper有如下几点:

ViewDragHelper.Callback是连接ViewDragHelper与view之间的桥梁(这个view一般是指拥子view的容器即parentView);

ViewDragHelper的实例是通过静态工厂方法创建的;

你能够指定拖动的方向;

 ViewDragHelper可以检测到是否触及到边缘;

ViewDragHelper并不是直接作用于要被拖动的View,而是使其控制的视图容器中的子View可以被拖动,如果要指定某个子view的行为,需要在Callback中想办法;

ViewDragHelper的本质其实是分析onInterceptTouchEvent和onTouchEvent的MotionEvent参数,然后根据分析的结果去改变一个容器中被拖动子View的位置( 通过offsetTopAndBottom(int offset)和offsetLeftAndRight(int offset)方法 ),他能在触摸的时候判断当前拖动的是哪个子View;

虽然ViewDragHelper的实例方法 ViewDragHelper create(ViewGroup forParent, Callback cb) 可以指定一个被ViewDragHelper处理拖动事件的对象 。

实现思路

  1. 自定义PuzzleLayout继承自RelativeLayout。

  2. 将PuzzleLayout的onInterceptTouchEvent和onTouchEvent交给ViewDragHelper来处理。

  3. 将拼图Bitmap按九宫格切割,生成ImageView添加到PuzzleLayout并进行排列。

  4. 创建ImageView的对应数据模型。

  5. ViewDragHelper.Callback控制滑动边界的实现。

  6. 打乱ImageView的摆放位置。

下面介绍一下以上5步的具体实现细节。

创建一个PuzzleLayout继承自RelativeLayout。

public class PuzzleLayout extends RelativeLayout { public PuzzleLayout(Context context) {   super(context);  }   public PuzzleLayout(Context context, AttributeSet attrs) {   super(context, attrs);  }   public PuzzleLayout(Context context, AttributeSet attrs, int defStyleAttr) {  }}

将PuzzleLayout的onInterceptTouchEvent和onTouchEvent交给ViewDragHelper来处理。

这里我们会用到ViewDragHelper这个处理手势滑动的神器。
在使用之前我们先简单的了解一下它的相关函数。

public static ViewDragHelper create(ViewGroup forParent, float sensitivity, Callback cb)

上面这个是创建一个ViewDragHelper的静态函数,根据注释我们可以了解到:

  1. 第一个参数是当前的ViewGroup。

  2. 第二个参数是检测拖动开始的灵敏度,1.0f为正常值。

  3. 第三个参数Callback,是ViewDragHelper给ViewGroup的回调。

这里我们主要来看看Callback这个参数,Callback会在手指触摸当前ViewGroup的过程中不断返回解析到的相关事件和状态,并获取ViewGroup返回给ViewDragHelper的状态,来决定接下来的操作是否需要执行,从而达到了在ViewGroup中管理和控制ViewDragHelper的目的。

Callback的方法很多,这里主要介绍本文用到的几个方法

public abstract boolean tryCaptureView(View child, int pointerId)

尝试捕获当前手指触摸到的子view, 返回true 允许捕获,false不捕获。

public int clampViewPositionHorizontal(View child, int left, int dx)

控制childView在水平方向的滑动,主要用来限定childView滑动的左右边界。

public int clampViewPositionVertical(View child, int top, int dy)

控制childView在垂直方向的滑动,主要用来限定childView滑动的上下边界。

public void onViewReleased(View releasedChild, float xvel, float yvel)

当手指从childView上离开时回调。

有了以上这些函数,我们的拼图游戏大致就可以做出来了,通过ViewDragHelper.create()来创建一个ViewDragHelper,通过Callback中tryCaptureView来控制当前触摸的子view是否可以滑动,clampViewPositionHorizontal、clampViewPositionVertical来控制水平方向和垂直方向的移动边界,具体的方法实现会在后面讲到。

public class PuzzleLayout extends RelativeLayout {  private ViewDragHelper viewDragHelper;  public PuzzleLayout(Context context) {    super(context);    init();  }  public PuzzleLayout(Context context, AttributeSet attrs) {    super(context, attrs);    init();  }  public PuzzleLayout(Context context, AttributeSet attrs, int defStyleAttr) {    super(context, attrs, defStyleAttr);    init();  }  private void init() {    getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {      @Override      public boolean onPreDraw() {        mHeight = getHeight();        mWidth = getWidth();        getViewTreeObserver().removeOnPreDrawListener(this);        if(mDrawableId != 0 && mSquareRootNum != 0){          createChildren();        }        return false;      }    });    viewDragHelper = ViewDragHelper.create(this, 1.0f, new ViewDragHelper.Callback() {      @Override      public boolean tryCaptureView(View child, int pointerId) {        return true;      }      @Override      public int clampViewPositionHorizontal(View child, int left, int dx) {        return left;      }      @Override      public int clampViewPositionVertical(View child, int top, int dy) {        return top;      }      @Override      public void onViewReleased(View releasedChild, float xvel, float yvel) {      }    });  }  @Override  public boolean onInterceptTouchEvent(MotionEvent event){    return viewDragHelper.shouldInterceptTouchEvent(event);  }  @Override  public boolean onTouchEvent(MotionEvent event) {    viewDragHelper.processTouchEvent(event);    return true;  }}

第三步,将拼图Bitmap按九宫格切割,生成ImageView添加到PuzzleLayout并进行排列。

怎么在Android中使用ViewDragHelper实现一个拼图游戏

首先,外界需要传入一个切割参数mSquareRootNum做为宽和高的切割份数,我们需要获取PuzzleLayout的宽和高,然后计算出每一块的宽mItemWidth和高mItemHeight, 将Bitmap等比例缩放到和PuzzleLayout大小相等,然后将图片按照类似上面这张图所标的形式进行切割,生成mSquareRootNum*mSquareRootNum份Bitmap,每个Bitmap对应创建一个ImageView载体添加到PuzzleLayout中,并进行布局排列。

创建子view, mHelper是封装的用来操作对应数据模型的帮助类DataHelper。

private void createChildren(){  mHelper.setSquareRootNum(mSquareRootNum);  DisplayMetrics dm = getResources().getDisplayMetrics();  BitmapFactory.Options options = new BitmapFactory.Options();  options.inDensity = dm.densityDpi;  Bitmap resource = BitmapFactory.decodeResource(getResources(), mDrawableId, options);  Bitmap bitmap = BitmapUtil.zoomImg(resource, mWidth, mHeight);  resource.recycle();  mItemWidth = mWidth / mSquareRootNum;  mItemHeight = mHeight / mSquareRootNum;  for (int i = 0; i < mSquareRootNum; i++){    for (int j = 0; j < mSquareRootNum; j++){      Log.d(TAG, "mItemWidth * x " + (mItemWidth * i));      Log.d(TAG, "mItemWidth * y " + (mItemWidth * j));      ImageView iv = new ImageView(getContext());      iv.setScaleType(ImageView.ScaleType.FIT_XY);      LayoutParams lp = new LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);      lp.leftMargin = j * mItemWidth;      lp.topMargin = i * mItemHeight;      iv.setLayoutParams(lp);      Bitmap b = Bitmap.createBitmap(bitmap, lp.leftMargin, lp.topMargin, mItemWidth, mItemHeight);      iv.setImageBitmap(b);      addView(iv);    }  }}

第四步,创建ImageView的对应数据模型。

public class Block {  public Block(int position, int vPosition, int hPosition){    this.position = position;    this.vPosition = vPosition;    this.hPosition = hPosition;  }  public int position;  public int vPosition;  public int hPosition;}

DataHelper.class

子View在父类的index与mHelper中model在models的index一一对应

class DataHelper {  static final int N = -1;  static final int L = 0;  static final int T = 1;  static final int R = 2;  static final int B = 3;  private static final String TAG = DataHelper.class.getSimpleName();  private int squareRootNum;  private List<Block> models;  DataHelper(){    models = new ArrayList<>();  }  private void reset() {    models.clear();    int position = 0;    for (int i = 0; i< squareRootNum; i++){      for (int j = 0; j < squareRootNum; j++){        models.add(new Block(position, i, j));        position ++;      }    }  }  void setSquareRootNum(int squareRootNum){    this.squareRootNum = squareRootNum;    reset();  }}

第五步,ViewDragHelper.Callback控制滑动边界的实现。

tryCaptureView的实现

public boolean tryCaptureView(View child, int pointerId) {      int index = indexOfChild(child);      return mHelper.getScrollDirection(index) != DataHelper.N;    }

DataHelper的getScrollDirection函数

int getScrollDirection(int index){  Block model = models.get(index);  int position = model.position;  //获取当前view所在位置的坐标 x y    int x = position % squareRootNum;  int y = position / squareRootNum;  int invisibleModelPosition = models.get(0).position;    if(x != 0 && invisibleModelPosition == position - 1)    return L;  if(x != squareRootNum - 1 && invisibleModelPosition == position + 1)    return R;  if(y != 0 && invisibleModelPosition == position - squareRootNum)    return T;  if(y != squareRootNum - 1 && invisibleModelPosition == position + squareRootNum)    return B;  return N;}

clampViewPositionHorizontal的实现细节,获取滑动方向左或右,再控制对应的滑动区域。

public int clampViewPositionHorizontal(View child, int left, int dx) {      int index = indexOfChild(child);      int position = mHelper.getModel(index).position;      int selfLeft = (position % mSquareRootNum) * mItemWidth;      int leftEdge = selfLeft - mItemWidth;      int rightEdge = selfLeft + mItemWidth;      int direction = mHelper.getScrollDirection(index);      //Log.d(TAG, "left " + left + " index" + index + " dx " + dx + " direction " + direction);      switch (direction){        case DataHelper.L:          if(left <= leftEdge)            return leftEdge;          else if(left >= selfLeft)            return selfLeft;          else            return left;        case DataHelper.R:          if(left >= rightEdge)            return rightEdge;          else if (left <= selfLeft)            return selfLeft;          else            return left;        default:          return selfLeft;      }    }

clampViewPositionVertical的实现细节,获取滑动方向上或下,再控制对应的滑动区域。

public int clampViewPositionVertical(View child, int top, int dy) {      int index = indexOfChild(child);      Block model = mHelper.getModel(index);      int position = model.position;      int selfTop = (position / mSquareRootNum) * mItemHeight;      int topEdge = selfTop - mItemHeight;      int bottomEdge = selfTop + mItemHeight;      int direction = mHelper.getScrollDirection(index);      //Log.d(TAG, "top " + top + " index " + index + " direction " + direction);      switch (direction){        case DataHelper.T:          if(top <= topEdge)            return topEdge;          else if (top >= selfTop)            return selfTop;          else            return top;        case DataHelper.B:          if(top >= bottomEdge)            return bottomEdge;          else if (top <= selfTop)            return selfTop;          else            return top;        default:          return selfTop;      }    }

onViewReleased的实现,当松手时,不可见View和松开的View之间进行布局参数交换,同时对应的model之间也需要通过swapValueWithInvisibleModel函数进行数据交换。

public void onViewReleased(View releasedChild, float xvel, float yvel) {      Log.d(TAG, "xvel " + xvel + " yvel " + yvel);      int index = indexOfChild(releasedChild);      boolean isCompleted = mHelper.swapValueWithInvisibleModel(index);      Block item = mHelper.getModel(index);      viewDragHelper.settleCapturedViewAt(item.hPosition * mItemWidth, item.vPosition * mItemHeight);      View invisibleView = getChildAt(0);      ViewGroup.LayoutParams layoutParams = invisibleView.getLayoutParams();      invisibleView.setLayoutParams(releasedChild.getLayoutParams());      releasedChild.setLayoutParams(layoutParams);      invalidate();      if(isCompleted){        invisibleView.setVisibility(VISIBLE);        mOnCompleteCallback.onComplete();      }    }

viewDragHelper.settleCapturedViewAt和viewDragHelper.continueSettling配合实现松手后的动画效果。

PuzzleLayout重写computeScroll函数。

@Overridepublic void computeScroll() {  if(viewDragHelper.continueSettling(true)) {    invalidate();  }}

swapValueWithInvisibleModel函数,每次交换完成后会return拼图是否完成

boolean swapValueWithInvisibleModel(int index){  Block fORMModel = models.get(index);  Block invisibleModel = models.get(0);  swapValue(formModel, invisibleModel);  return isCompleted();}private void swapValue(Block formModel, Block invisibleModel) {  int position = formModel.position;  int hPosition = formModel.hPosition;  int vPosition = formModel.vPosition;  formModel.position = invisibleModel.position;  formModel.hPosition = invisibleModel.hPosition;  formModel.vPosition = invisibleModel.vPosition;  invisibleModel.position = position;  invisibleModel.hPosition = hPosition;  invisibleModel.vPosition = vPosition;}private boolean isCompleted(){  int num = squareRootNum * squareRootNum;  for (int i = 0; i < num; i++){    Block model = models.get(i);    if(model.position != i){      return false;    }  }  return true;}

第六步,打乱ImageView的摆放位置。

这里不能随意打乱顺序,否则你可能永远也不能复原拼图了,这里使用的办法是每次在不可见View附近随机找一个View与不可见View进行位置交换,这里的位置交换指的是布局参数的交换,同时对应的数据模型也需要进行数据交换。

public void randomOrder(){  int num = mSquareRootNum * mSquareRootNum * 8;  View invisibleView = getChildAt(0);  View neighbor;  for (int i = 0; i < num; i ++){    int neighborPosition = mHelper.findNeighborIndexOfInvisibleModel();    ViewGroup.LayoutParams invisibleLp = invisibleView.getLayoutParams();    neighbor = getChildAt(neighborPosition);    invisibleView.setLayoutParams(neighbor.getLayoutParams());    neighbor.setLayoutParams(invisibleLp);    mHelper.swapValueWithInvisibleModel(neighborPosition);  }  invisibleView.setVisibility(INVISIBLE);}

DataHelper中findNeighborIndexOfInvisibleModel函数

public int findNeighborIndexOfInvisibleModel() {  Block invisibleModel = models.get(0);  int position = invisibleModel.position;  int x = position % squareRootNum;  int y = position / squareRootNum;  int direction = new Random(System.nanoTime()).nextInt(4);  Log.d(TAG, "direction " + direction);  switch (direction){    case L:      if(x != 0)        return getIndexByCurrentPosition(position - 1);    case T:      if(y != 0)        return getIndexByCurrentPosition(position - squareRootNum);    case R:      if(x != squareRootNum - 1)        return getIndexByCurrentPosition(position + 1);    case B:      if(y != squareRootNum - 1)        return getIndexByCurrentPosition(position + squareRootNum);  }  return findNeighborIndexOfInvisibleModel();}private int getIndexByCurrentPosition(int currentPosition){  int num = squareRootNum * squareRootNum;  for (int i = 0; i < num; i++) {    if(models.get(i).position == currentPosition)      return i;  }  return -1;}

以上就是怎么在Android中使用ViewDragHelper实现一个拼图游戏,小编相信有部分知识点可能是我们日常工作会见到或用到的。希望你能通过这篇文章学到更多知识。更多详情敬请关注编程网精选频道。

--结束END--

本文标题: 怎么在Android中使用ViewDragHelper实现一个拼图游戏

本文链接: https://lsjlt.com/news/220296.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

猜你喜欢
  • 怎么在Android中使用ViewDragHelper实现一个拼图游戏
    本篇文章给大家分享的是有关怎么在Android中使用ViewDragHelper实现一个拼图游戏,小编觉得挺实用的,因此分享给大家学习,希望大家阅读完这篇文章后可以有所收获,话不多说,跟着小编一起来看看吧。ViewDragHelper其实V...
    99+
    2023-05-30
    android viewdraghelper
  • 怎么用Android实现拼图游戏
    小编给大家分享一下怎么用Android实现拼图游戏,希望大家阅读完这篇文章之后都有所收获,下面让我们一起去探讨吧!具体内容如下源码package packageName;import android.os.Bundle;...
    99+
    2023-06-29
  • 怎么在Android中使用WebSocket实现一个多人游戏
    今天就跟大家聊聊有关怎么在Android中使用WebSocket实现一个多人游戏,可能很多人都不太了解,为了让大家更加了解,小编给大家总结了以下内容,希望大家根据这篇文章可以有所收获。webSocket流程客户端开始连接------->...
    99+
    2023-05-30
    android websocket
  • java中拼图游戏怎么实现
    这篇文章主要介绍了java中拼图游戏怎么实现,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。直接上效果图:1.所需技术java基础java的GUI相关技术2.具体实现2.1&n...
    99+
    2023-06-29
  • C#怎么实现拼图游戏
    这篇文章主要介绍“C#怎么实现拼图游戏”,在日常操作中,相信很多人在C#怎么实现拼图游戏问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”C#怎么实现拼图游戏”的疑惑有所帮助!接下来,请跟着小编一起来学习吧!本文...
    99+
    2023-06-20
  • 怎么使用js编写实现拼图游戏
    这篇文章主要讲解了“怎么使用js编写实现拼图游戏”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“怎么使用js编写实现拼图游戏”吧!目标使用原生js编写一个拼图游戏,我这里写了两种拼图的方法。一...
    99+
    2023-07-02
  • 怎么用Python实现拼图小游戏
    本篇内容主要讲解“怎么用Python实现拼图小游戏”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“怎么用Python实现拼图小游戏”吧!开发工具Python版本:...
    99+
    2024-04-02
  • 使用js编写实现拼图游戏
    本文实例为大家分享了用js编写实现拼图游戏的具体代码,供大家参考,具体内容如下 目标 使用原生js编写一个拼图游戏,我这里写了两种拼图的方法。一种是拖拽拼图,一种是经典的九宫格拼图,...
    99+
    2024-04-02
  • 怎么在HTML5中利用拖拽功能实现拼图游戏
    本篇文章为大家展示了怎么在HTML5中利用拖拽功能实现拼图游戏,内容简明扼要并且容易理解,绝对能使你眼前一亮,通过这篇文章的详细介绍希望你能有所收获。<!--代码如下,最下面给出了我测试用的9张250*250的图片切片-->&l...
    99+
    2023-06-09
  • 怎么在python中使用tkinter实现一个扫雷游戏
    怎么在python中使用tkinter实现一个扫雷游戏?很多新手对此不是很清楚,为了帮助大家解决这个难题,下面小编将为大家详细讲解,有这方面需求的人可以来学习下,希望你能有所收获。实现代码# 导入所需库from tkin...
    99+
    2023-06-15
  • 怎么在java中使用swing实现一个扫雷游戏
    这篇文章给大家介绍怎么在java中使用swing实现一个扫雷游戏,内容非常详细,感兴趣的小伙伴们可以参考借鉴,希望对大家能有所帮助。版本1:package awtDemo;import java.awt.event.Ac...
    99+
    2023-05-30
    java swing
  • ViewDragHelper怎么在Android项目中使用
    本篇文章为大家展示了 ViewDragHelper怎么在Android项目中使用,内容简明扼要并且容易理解,绝对能使你眼前一亮,通过这篇文章的详细介绍希望你能有所收获。ViewDragHelper是support.v4下提供的用于处理拖拽滑...
    99+
    2023-05-30
    android viewdraghelper
  • Linux在怎么使用curses图形库实现一个贪吃蛇游戏
    这篇文章将为大家详细讲解有关Linux在怎么使用curses图形库实现一个贪吃蛇游戏,文章内容质量较高,因此小编分享给大家做个参考,希望大家阅读完这篇文章后对相关知识有一定的了解。在ubuntu下安装curses图形库命令sudo ...
    99+
    2023-06-15
  • 怎么在java中使用HashMap实现一个斗地主游戏
    本篇文章给大家分享的是有关怎么在java中使用HashMap实现一个斗地主游戏,小编觉得挺实用的,因此分享给大家学习,希望大家阅读完这篇文章后可以有所收获,话不多说,跟着小编一起来看看吧。1、准备牌:每张扑克牌牌由花色和数字两部分组成。可以...
    99+
    2023-06-06
  • 怎么在VUE中使用Canvas实现一个五子棋游戏
    怎么在VUE中使用Canvas实现一个五子棋游戏?相信很多没有经验的人对此束手无策,为此本文总结了问题出现的原因和解决方法,通过这篇文章希望你能解决这个问题。vue是什么Vue是一套用于构建用户界面的渐进式JavaScript框架,Vue与...
    99+
    2023-06-15
  • 使用canvas怎么实现一个拼图功能
    使用canvas怎么实现一个拼图功能?很多新手对此不是很清楚,为了帮助大家解决这个难题,下面小编将为大家详细讲解,有这方面需求的人可以来学习下,希望你能有所收获。实现的思路其实挺简单的,主要是通过服务端获取图片链接,图片宽度,图片高度,然后...
    99+
    2023-06-09
  • 使用Python3怎么实现一个射击游戏
    今天就跟大家聊聊有关使用Python3怎么实现一个射击游戏,可能很多人都不太了解,为了让大家更加了解,小编给大家总结了以下内容,希望大家根据这篇文章可以有所收获。from __future__ import d...
    99+
    2023-06-14
  • 怎么在HTML5中使用WebGL实现一个俄罗斯方块游戏
    怎么在HTML5中使用WebGL实现一个俄罗斯方块游戏?相信很多没有经验的人对此束手无策,为此本文总结了问题出现的原因和解决方法,通过这篇文章希望你能解决这个问题。代码实现首先,先完成 2D 小游戏在查看官方文档的过程中,了解到 HT 的组...
    99+
    2023-06-09
  • Android中怎么利用ViewDragHelper实现一个淘宝拖动加载效果
    本篇文章为大家展示了Android中怎么利用ViewDragHelper实现一个淘宝拖动加载效果,内容简明扼要并且容易理解,绝对能使你眼前一亮,通过这篇文章的详细介绍希望你能有所收获。创建自定义ViewGroup:package ...
    99+
    2023-05-30
    android viewdraghelper
  • 怎么使用Matlab制作一款真正的拼图小游戏
    这篇文章主要介绍怎么使用Matlab制作一款真正的拼图小游戏,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!效果:简单原理介绍:1构造0,1矩阵作为每片拼图的透明度,可以构造出不规则形状的拼图(image函数有alph...
    99+
    2023-06-15
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作