【移動應(yīng)用開發(fā)技術(shù)】怎么在Android中使用ViewDragHelper實(shí)現(xiàn)一個拼圖游戲_第1頁
【移動應(yīng)用開發(fā)技術(shù)】怎么在Android中使用ViewDragHelper實(shí)現(xiàn)一個拼圖游戲_第2頁
【移動應(yīng)用開發(fā)技術(shù)】怎么在Android中使用ViewDragHelper實(shí)現(xiàn)一個拼圖游戲_第3頁
【移動應(yīng)用開發(fā)技術(shù)】怎么在Android中使用ViewDragHelper實(shí)現(xiàn)一個拼圖游戲_第4頁
【移動應(yīng)用開發(fā)技術(shù)】怎么在Android中使用ViewDragHelper實(shí)現(xiàn)一個拼圖游戲_第5頁
已閱讀5頁,還剩13頁未讀 繼續(xù)免費(fèi)閱讀

下載本文檔

版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進(jìn)行舉報或認(rèn)領(lǐng)

文檔簡介

【移動應(yīng)用開發(fā)技術(shù)】怎么在Android中使用ViewDragHelper實(shí)現(xiàn)一個拼圖游戲

本篇文章給大家分享的是有關(guān)怎么在Android中使用ViewDragHelper實(shí)現(xiàn)一個拼圖游戲,在下覺得挺實(shí)用的,因此分享給大家學(xué)習(xí),希望大家閱讀完這篇文章后可以有所收獲,話不多說,跟著在下一起來看看吧。ViewDragHelper其實(shí)ViewDragHelper并不是第一個用于分析手勢處理的類,gesturedetector也是,但是在和拖動相關(guān)的手勢分析方面gesturedetector只能說是勉為其難。關(guān)于ViewDragHelper有如下幾點(diǎn):ViewDragHelper.Callback是連接ViewDragHelper與view之間的橋梁(這個view一般是指擁子view的容器即parentView);ViewDragHelper的實(shí)例是通過靜態(tài)工廠方法創(chuàng)建的;你能夠指定拖動的方向;

ViewDragHelper可以檢測到是否觸及到邊緣;ViewDragHelper并不是直接作用于要被拖動的View,而是使其控制的視圖容器中的子View可以被拖動,如果要指定某個子view的行為,需要在Callback中想辦法;ViewDragHelper的本質(zhì)其實(shí)是分析onInterceptTouchEvent和onTouchEvent的MotionEvent參數(shù),然后根據(jù)分析的結(jié)果去改變一個容器中被拖動子View的位置(通過offsetTopAndBottom(intoffset)和offsetLeftAndRight(intoffset)方法),他能在觸摸的時候判斷當(dāng)前拖動的是哪個子View;雖然ViewDragHelper的實(shí)例方法ViewDragHelpercreate(ViewGroupforParent,Callbackcb)可以指定一個被ViewDragHelper處理拖動事件的對象。實(shí)現(xiàn)思路自定義PuzzleLayout繼承自RelativeLayout。將PuzzleLayout的onInterceptTouchEvent和onTouchEvent交給ViewDragHelper來處理。將拼圖Bitmap按九宮格切割,生成ImageView添加到PuzzleLayout并進(jìn)行排列。創(chuàng)建ImageView的對應(yīng)數(shù)據(jù)模型。ViewDragHelper.Callback控制滑動邊界的實(shí)現(xiàn)。打亂ImageView的擺放位置。下面介紹一下以上5步的具體實(shí)現(xiàn)細(xì)節(jié)。第一步:創(chuàng)建一個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這個處理手勢滑動的神器。在使用之前我們先簡單的了解一下它的相關(guān)函數(shù)。/**

*

Factory

method

to

create

a

new

ViewDragHelper.

*

*

@param

forParent

Parent

view

to

monitor

*

@param

sensitivity

Multiplier

for

how

sensitive

the

helper

*

should

be

about

detecting

the

start

of

a

drag.

*

Larger

values

are

more

sensitive.

1.0f

is

normal.

*

@param

cb

Callback

to

provide

information

and

receive

events

*

@return

a

new

ViewDragHelper

instance

*/

public

static

ViewDragHelper

create(ViewGroup

forParent,

float

sensitivity,

Callback

cb)上面這個是創(chuàng)建一個ViewDragHelper的靜態(tài)函數(shù),根據(jù)注釋我們可以了解到:第一個參數(shù)是當(dāng)前的ViewGroup。第二個參數(shù)是檢測拖動開始的靈敏度,1.0f為正常值。第三個參數(shù)Callback,是ViewDragHelper給ViewGroup的回調(diào)。這里我們主要來看看Callback這個參數(shù),Callback會在手指觸摸當(dāng)前ViewGroup的過程中不斷返回解析到的相關(guān)事件和狀態(tài),并獲取ViewGroup返回給ViewDragHelper的狀態(tài),來決定接下來的操作是否需要執(zhí)行,從而達(dá)到了在ViewGroup中管理和控制ViewDragHelper的目的。Callback的方法很多,這里主要介紹本文用到的幾個方法public

abstract

boolean

tryCaptureView(View

child,

int

pointerId)嘗試捕獲當(dāng)前手指觸摸到的子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)當(dāng)手指從childView上離開時回調(diào)。有了以上這些函數(shù),我們的拼圖游戲大致就可以做出來了,通過ViewDragHelper.create()來創(chuàng)建一個ViewDragHelper,通過Callback中tryCaptureView來控制當(dāng)前觸摸的子view是否可以滑動,clampViewPositionHorizontal、clampViewPositionVertical來控制水平方向和垂直方向的移動邊界,具體的方法實(shí)現(xiàn)會在后面講到。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)

{

viewDragHcessTouchEvent(event);

return

true;

}

}第三步,將拼圖Bitmap按九宮格切割,生成ImageView添加到PuzzleLayout并進(jìn)行排列。首先,外界需要傳入一個切割參數(shù)mSquareRootNum做為寬和高的切割份數(shù),我們需要獲取PuzzleLayout的寬和高,然后計(jì)算出每一塊的寬mItemWidth和高mItemHeight,將Bitmap等比例縮放到和PuzzleLayout大小相等,然后將圖片按照類似上面這張圖所標(biāo)的形式進(jìn)行切割,生成mSquareRootNum*mSquareRootNum份Bitmap,每個Bitmap對應(yīng)創(chuàng)建一個ImageView載體添加到PuzzleLayout中,并進(jìn)行布局排列。創(chuàng)建子view,mHelper是封裝的用來操作對應(yīng)數(shù)據(jù)模型的幫助類DataHelper。/**

*

將子View

index與mHelper中models的index一一對應(yīng),

*

每次在交換子View位置的時候model同步更新currentPosition。

*/

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);

}

}

}第四步,創(chuàng)建ImageView的對應(yīng)數(shù)據(jù)模型。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一一對應(yīng)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控制滑動邊界的實(shí)現(xiàn)。tryCaptureView的實(shí)現(xiàn)public

boolean

tryCaptureView(View

child,

int

pointerId)

{

int

index

=

indexOfChild(child);

return

mHelper.getScrollDirection(index)

!=

DataHelper.N;

}DataHelper的getScrollDirection函數(shù)/**

*

獲取索引處model的可移動方向,不能移動返回

-1。

*/

int

getScrollDirection(int

index){

Block

model

=

models.get(index);

int

position

=

model.position;

//獲取當(dāng)前view所在位置的坐標(biāo)

x

y

/*

*

*

*

*

*

*

*

o

*

*

*

*

*

*

*

*

*

*

*

*

*/

int

x

=

position

%

squareRootNum;

int

y

=

position

/

squareRootNum;

int

invisibleModelPosition

=

models.get(0).position;

/*

*

判斷當(dāng)前位置是否可以移動,如果可以移動就return可移動的方向。

*/

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的實(shí)現(xiàn)細(xì)節(jié),獲取滑動方向左或右,再控制對應(yīng)的滑動區(qū)域。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的實(shí)現(xiàn)細(xì)節(jié),獲取滑動方向上或下,再控制對應(yīng)的滑動區(qū)域。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的實(shí)現(xiàn),當(dāng)松手時,不可見View和松開的View之間進(jìn)行布局參數(shù)交換,同時對應(yīng)的model之間也需要通過swapValueWithInvisibleModel函數(shù)進(jìn)行數(shù)據(jù)交換。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配合實(shí)現(xiàn)松手后的動畫效果。PuzzleLayout重寫computeScroll函數(shù)。@Override

public

void

computeScroll()

{

if(viewDragHelper.continueSettling(true))

{

invalidate();

}

}swapValueWithInvisibleModel函數(shù),每次交換完成后會return拼圖是否完成/**

*

將索引出的model的值與不可見

*

model的值互換。

*/

boolean

swapValueWithInvisibleModel(int

index){

Block

formModel

=

models.get(index);

Block

invisibleModel

=

models.get(0);

swapValue(formModel,

invisibleModel);

return

isCompleted();

}

/**

*

交換兩個model的值

*/

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的擺放位置。這里不能隨意打亂順序,否則你可能永遠(yuǎn)也不能復(fù)原拼圖了,這里使用的辦法是每次在不可見View附近隨機(jī)找一個View與不可見View進(jìn)行位置交換,這里的位置交換指的是布局參數(shù)的交換,同時對應(yīng)的數(shù)據(jù)模型也需要進(jìn)行數(shù)據(jù)交換。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

invis

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負(fù)責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

評論

0/150

提交評論