版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/u012964944/article/details/70172885
之前的一篇文章《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);
}
}
完整的代码放在了github上:https://github.com/1993hzw/Androids,谢谢大伙的支持!