android圖片滾動選擇器的實現
阿新 • • 發佈:2019-02-11
之前的一篇文章《Android自定義view——滾動選擇器》(沒看過的同學建議先去了解一下)介紹了滾動選擇器的原理,並實現了字串選擇器。現在要講講圖片選擇器的實現,以及通過選擇器實現老虎機效果。
圖片選擇器
跟字串選擇器(StringScrollPicker)一樣,圖片選擇器繼承了ScrollPickerView,並在drawItem()方法裡面實現圖片的繪製。這裡邊提供了三種圖片繪製模式:填充、居中、指定大小。
public class BitmapScrollPicker extends ScrollPickerView<Bitmap> {
/**
* 圖片繪製模式:填充
*/
public final static int DRAW_MODE_FULL = 1;
/**
* 圖片繪製模式:居中
*/
public final static int DRAW_MODE_CENTER = 2;
/**
* 圖片繪製模式:指定大小
*/
public final static int DRAW_MODE_SPECIFIED_SIZE = 3;
...
@Override
public void drawItem(Canvas canvas, List<Bitmap> data, int position, int relative, float moveLength, float top) {
...
}
...
}
老虎機
其實老虎機就是三個圖片滾動選擇器的組合,另外加上自動滾動及控制每個滾動器的結果。下面是老虎機SlotMachine的簡單佈局,主要是把三個滾動選擇器的長度設定得比父容器稍高大一點,使得最上面和最下面的獎勵只顯示一半。
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width ="match_parent"
android:layout_height="250dp"
android:layout_gravity="center_horizontal"
android:padding="24dp"
>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="288dp"
android:layout_gravity="center_vertical"
android:layout_margin="6dp"
android:layout_weight="1"
android:orientation="horizontal">
<cn.forward.androids.views.BitmapScrollPicker
android:id="@+id/slot_view_01"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1"
/>
<FrameLayout
android:layout_width="2dp"
android:layout_height="match_parent"
android:layout_gravity="center_vertical"
android:background="#bfbfbf"/>
<cn.forward.androids.views.BitmapScrollPicker
android:id="@+id/slot_view_02"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1"
/>
<FrameLayout
android:layout_width="2dp"
android:layout_height="match_parent"
android:layout_gravity="center_vertical"
android:background="#bfbfbf"/>
<cn.forward.androids.views.BitmapScrollPicker
android:id="@+id/slot_view_03"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1"
/>
</LinearLayout>
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@drawable/shape_slot_machine_border"
/>
</FrameLayout>
為了更接近老虎機的效果,當結果為沒抽中時,使得其中兩個選擇器的獎勵儘量相同,給玩家一種“差一點就中了”的感覺。同時,提供了回撥介面給呼叫者來確定最終的結果,並採取相應的彌補措施。老虎機的關鍵程式碼如下:
/**
* 老虎機
*/
public class SlotMachine extends FrameLayout implements ScrollPickerView.OnSelectedListener {
// 滾動的時間
private static int DURATION01 = 3000;
private static int DURATION02 = 3500;
private static int DURATION03 = 4000;
private final static ArrayList<Integer> mDurationList = new ArrayList<Integer>(Arrays.asList(DURATION01, DURATION02, DURATION03));
...
@Override
public void onSelected(ScrollPickerView slotView, int position) {
if (mIsPlaying) {
mFinishedCounter++;
if (slotView == mSlot01) {
mSelectedArray[0] = position;
} else if (slotView == mSlot02) {
mSelectedArray[1] = position;
} else if (slotView == mSlot03) {
mSelectedArray[2] = position;
}
if (mFinishedCounter >= 3) {
mFinishedCounter = 0;
if (mSlotMachineListener != null) {
boolean win = false;
boolean makeup = false;
if (mSelectedArray[0] == mSelectedArray[1] && mSelectedArray[0] == mSelectedArray[2]) { // win
// 是否取消中獎,採用彌補動畫,使之變成不中獎的結果
win = mSlotMachineListener.acceptWinResult(mSelectedArray[0]);
makeup = !win;
}
final boolean finalMakeup = makeup;
Runnable task = new Runnable() {
public void run() {
if (finalMakeup) {
mSelectedArray[2] = mSlot03.getSelectedPosition();
}
mSlotMachineListener.onFinish(mSelectedArray[0], mSelectedArray[1], mSelectedArray[2]);
mIsPlaying = false;
}
};
if (makeup) {
makeUpPurchaseFailed(mSelectedArray[2]);
// 等待彌補動畫結束
ThreadUtil.getInstance().runOnMainThread(task, 1200);
} else {
task.run();
}
}
}
}
}
/**
* 開始滾動,
*
* @param prizePosition 獎品的索引,如果prizePosition<0或者 prizePosition>=總的獎品數,則表示不中獎
*/
public boolean play(int prizePosition) {
if (!isClickable() || mIsPlaying) {
return false;
}
mFinishedCounter = 0;
mIsPlaying = true;
int slot01, slot02, slot03;
int duration01, duration02, duration03;
// Collections.shuffle(mDurationList);
duration01 = mDurationList.get(0);
duration02 = mDurationList.get(1);
duration03 = mDurationList.get(2);
if (prizePosition < 0 || prizePosition >= mPrizeList.size()) { // 不中獎,控制三個中有兩個相同
// pos01表示時間最短的停留位置,pos03表示時間最長
int pos01, pos02, pos03;
pos01 = mRandom.nextInt(mPrizeList.size());
if (mRandom.nextInt(3) == 0) { // 01,02相同的概率為1/3
pos02 = pos01;
pos03 = mRandom.nextInt(mPrizeList.size());
} else {
pos02 = mRandom.nextInt(mPrizeList.size());
if (mRandom.nextInt(4) == 0) { // 01,03相同的概率為1/4
pos03 = pos01;
} else {
pos03 = mRandom.nextInt(mPrizeList.size());
}
}
if (pos01 == pos02 && pos01 == pos03) {
pos01 = (pos01 + 1) % mPrizeList.size();
}
// 按照時間排序老虎機的視窗,如[1,3,2]表示slot01的時間最短,接著是slot03,slot02
HashMap<Integer, Integer> map = new HashMap<Integer, Integer>();
map.put(mDurationList.indexOf(DURATION01) + 1, pos01); // 時間最短的那個slot停留的位置
map.put(mDurationList.indexOf(DURATION02) + 1, pos02);
map.put(mDurationList.indexOf(DURATION03) + 1, pos03);
slot01 = map.get(1);
slot02 = map.get(2);
slot03 = map.get(3);
} else {
slot01 = slot02 = slot03 = prizePosition;
}
mSlot01.autoScrollFast(slot01, duration01);
mSlot02.autoScrollFast(slot02, duration02);
mSlot03.autoScrollFast(slot03, duration03);
return true;
}
/**
* 彌補購買失敗,使其中一個往下滾動一列,造成未抽中的假象
*
* @param prizePosition
*/
public void makeUpPurchaseFailed(int prizePosition) {
int moveY = mSlot03.getItemHeight();
mSlot03.autoScrollTo(moveY, 1200, new LinearInterpolator(), false);
}
public interface SlotMachineListener {
/**
* 滾動結束時回撥
* @param pos01
* @param pos02
* @param pos03
*/
void onFinish(int pos01, int pos02, int pos03);
/**
* 是否接受該次中獎結果
* @param position
* @return 返回true則表示確認該次贏得獎品,false則表示取消該次獎品
*/
boolean acceptWinResult(int position);
}
}