




版權(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)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 科學(xué)城項(xiàng)目環(huán)境影響評估
- 防沙治沙光伏一體化項(xiàng)目區(qū)域選擇與環(huán)境分析
- 眼部檢查技術(shù)與操作要點(diǎn)培訓(xùn)
- 掛網(wǎng)工程合同范本
- 科技加持通過儀器監(jiān)測和調(diào)整腸道環(huán)境
- 鑄件采購合同范本
- 農(nóng)民養(yǎng)魚出售合同范本
- α-Hydroxy-flubromazolam-生命科學(xué)試劑-MCE
- p38α-inhibitor-7-生命科學(xué)試劑-MCE
- 奶茶店承租合同范本
- 2025年湖南工業(yè)職業(yè)技術(shù)學(xué)院單招職業(yè)傾向性測試題庫含答案
- 社會企業(yè)參與養(yǎng)老服務(wù)的模式與效果
- 2025年執(zhí)業(yè)醫(yī)師定期考核題庫及參考答案
- 2025年北京交通職業(yè)技術(shù)學(xué)院高職單招職業(yè)技能測試近5年??及鎱⒖碱}庫含答案解析
- 心理健康七個一主題活動方案
- 多元化票務(wù)系統(tǒng)設(shè)計(jì)-深度研究
- 絕緣墊技術(shù)規(guī)范說明
- 2024年菏澤職業(yè)學(xué)院高職單招語文歷年參考題庫含答案解析
- GB/T 22180-2024速凍裹衣魚
- 人教版地理七年級下冊7.1.1 亞洲的自然環(huán)境(課件33張)
- 《Python程序設(shè)計(jì)基礎(chǔ)教程(微課版)》全套教學(xué)課件
評論
0/150
提交評論