滑塊滑動&滑塊點擊&good、best、miss效果實現
實現思路大致是,設置滑塊(繼承自button)對象,兩個軌道每個軌道都對應一個ArrayList<EachButton>滑塊列表,每個滑塊都定義了它的軌道位置、滑動動畫、起始時間、滑動時間、滑動路徑的屬性。
遊戲頁面得到來自模式選擇頁面的會話,傳遞過來了音頻信息、模式信息,然後調用節奏點獲取的HandleData類來獲取音頻的節奏時間點N個,初始化N個滑塊,將‘節奏時間點-滑動時間’作爲滑塊的滑動動畫延遲時間(這樣滑到按鈕處時就剛好到那個樂點了),將模式信息中的速度、滑塊多少等來量化滑塊移動時間、節奏點個數的信息,然後開始全部滑塊的start方法來開啓動畫。
點擊下方的FREE按鈕會觸發方法,得到該按鈕所在軌道的最近一個滑塊的位置,與按鈕的位置作比較給出good、best級別,將該滑塊移出軌道隊列;如果滑塊動畫結束仍沒被點擊(還在隊列),則判斷爲miss,並移出隊列。
滑塊移動動畫核心代碼爲:
Path path=new Path();
path.moveTo(fromX,fromY);//設置path開始座標
path.lineTo(toX,toY);//設置path結束座標
animator=ObjectAnimator.ofFloat(this,
Property.of(EachButton.class,Float.class,"translationX"),
Property.of(EachButton.class,Float.class,"translationY"),
path);//設置animator是沿着path路徑這種方式移動的
animator.setDuration(time);//設置動畫時長,即整個移動過程的時間
animator.setStartDelay(startTime);//設置動畫延遲時間,到節奏點了再讓滑塊動
LinearInterpolator lin = new LinearInterpolator();//勻速運動
animator.setInterpolator(lin);
animator.addListener(new Animator.AnimatorListener() {
@Override
public void onAnimationStart(Animator animation) {
mlayout.addView(eachButton);
}
@Override
public void onAnimationEnd(Animator animation) {
mlayout.removeView(eachButton);
}
});
good、best、miss提示的彈出、淡化、消失的僞跳躍漸變效果的核心代碼爲:
View view = LayoutInflater.from(mcontext).inflate(R.layout.toast, null);//得到視圖裏的toast,更新toast的文本爲miss
TextView textView = view.findViewById(R.id.textView);
textView.setText("miss");
Toast toast = GameActivity.toast;//(設置一個toast變量,有初始樣式,每次點擊後更新它的TextView,並show()出來即可)
toast.setView(view);//更新toast的樣式
toast.setDuration(Toast.LENGTH_SHORT);
ObjectAnimator.ofArgb(textView, "textColor",
Color.parseColor("#ff333333"),
Color.parseColor("#00333333"))
.setDuration(1000)
.start();//設置toast的動畫顯示時間及顏色變化(灰色到灰色透明這種僞漸變效果)
toast.show();//展示這個toast
底部按鈕點擊時變色的核心代碼爲:
ObjectAnimator.ofArgb(bottoms[i], "backgroundColor",
Color.parseColor("#F3E77C"),
Color.parseColor("#F8E097"))
.setDuration(500)
.start();
toast.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/toast"
>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/textView"
android:text=""
android:textStyle="bold"
android:textSize="40sp"
android:shadowRadius="7.0"
android:shadowDx="-7"
android:shadowDy="7"
android:shadowColor="@color/colorSilver"
android:layout_marginBottom="125dp"
android:layout_gravity="center_horizontal"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
></TextView><!--這裏是constraintlayout,與底部左邊的距離依靠具體手機分辨率決定-->
</androidx.constraintlayout.widget.ConstraintLayout>
EachButton類
package com.example.free.Classes;
import android.animation.Animator;
import android.animation.ObjectAnimator;
import android.content.Context;
import android.content.Intent;
import android.graphics.Color;
import android.graphics.Path;
import android.util.Property;
import android.view.LayoutInflater;
import android.view.View;
import android.view.animation.LinearInterpolator;
import android.widget.TextView;
import android.widget.Toast;
import androidx.appcompat.widget.AppCompatButton;
import androidx.constraintlayout.widget.ConstraintLayout;
import com.example.free.GameActivity;
import com.example.free.R;
import com.example.free.ResultActivity;
import java.util.Timer;
import java.util.TimerTask;
public class EachButton extends AppCompatButton {
//這裏的座標位置都是用的像素px,因爲實時獲得滑塊的運動位置得到返回值是像素
int fromX=0;//滑塊移動的起始點x座標
int fromY=0;//起始點y座標
int toX=0;//滑塊移動的終止點x座標
int toY=0;//終止點y座標
int startTime=0;//開始移動的時間點
int time=0;//滑動過程的時間長度
int width=240;//px 滑塊寬
int height=120;//滑塊長
ConstraintLayout mlayout;//滑塊所在的視圖
Context mcontext;//滑塊所在的上下文活動
public ObjectAnimator animator;//動畫實例
public boolean state=true;//按鈕的狀態,是否還存在於隊列中
int path=0;//所在軌道
public EachButton(Context context,int _width,int _height,int fx,int fy,int tox,int toy,int st,int t,int p){
super(context);
this.fromX=fx;
this.fromY=fy;
this.toX=tox;
this.toY=toy;
this.startTime=st;
this.time=t;
this.path=p;
width=_width;
height=_height;
mcontext=context;
this.setBackgroundColor(0xaaF8E097);
this.setWidth(width);
this.setHeight(height);
this.layout(1000,0,1000+width,height);//layout裏的參數是滑塊初始位置的四個頂點的座標(左上x,左上y,右下x,右下y)
}
//通過設置滑塊動畫的延遲時間,比如20s時是一個節奏點,我們從一開始就初始化了滑塊的動畫,設置延遲時間爲20s
public void start(ConstraintLayout layout){
mlayout=layout;
Path path=new Path();
path.moveTo(fromX,fromY);//設置path開始座標
path.lineTo(toX,toY);//設置path結束座標
animator=ObjectAnimator.ofFloat(this,
Property.of(EachButton.class,Float.class,"translationX"),
Property.of(EachButton.class,Float.class,"translationY"),
path);//設置animator是沿着path路徑這種方式移動的
animator.setDuration(time);//設置動畫時長,即整個移動過程的時間
animator.setStartDelay(startTime);//設置動畫延遲時間,到節奏點了再讓滑塊動
LinearInterpolator lin = new LinearInterpolator();//勻速運動
animator.setInterpolator(lin);
final EachButton eachButton=this;//設置滑塊動畫監聽動作,等到動畫結束後"銷燬"該滑塊,方便進行下一次點擊計算,否則點擊動作識別的一直是第一個滑塊
animator.addListener(new Animator.AnimatorListener() {
@Override
public void onAnimationStart(Animator animation) {
mlayout.addView(eachButton);//
}
@Override
public void onAnimationEnd(Animator animation) {
mlayout.removeView(eachButton);//動畫結束移除該滑塊
//滑塊的消除(移出buttons隊列)由兩種行爲觸發,一種是點擊,一種是沒點到過去了的miss狀態,以下是第二種
if(eachButton.state) {//eachButton還在隊列裏,但動畫結束了,這意味着沒點擊成功
View view = LayoutInflater.from(mcontext).inflate(R.layout.toast, null);//得到視圖裏的toast,更新toast的文本爲miss
TextView textView = view.findViewById(R.id.textView);
textView.setText("miss");
Toast toast = GameActivity.toast;
toast.setView(view);//更新toast的樣式
toast.setDuration(Toast.LENGTH_SHORT);
ObjectAnimator.ofArgb(textView, "textColor",
Color.parseColor("#ff333333"),
Color.parseColor("#00333333"))
.setDuration(1000)
.start();//設置toast的動畫顯示時間及顏色變化(灰色到灰色透明這種僞漸變效果)
toast.show();//展示這個toast
GameActivity.buttons.get(eachButton.path).remove(0);//將button移出隊列
GameActivity.missTimes+=1;//miss次數加一
//如果最後一個滑塊是由miss結尾的,則在這裏結束隊列並且跳轉至成績結算頁面
if((GameActivity.buttons.get(0).size()+GameActivity.buttons.get(1).size()==0)&&GameActivity.activityState) {
final Intent iintent = new Intent(mcontext, ResultActivity.class);
iintent.putExtra("wholeScore",GameActivity.wholeScore);
iintent.putExtra("resultScore",GameActivity.resultScore);
iintent.putExtra("bestTimes",GameActivity.bestTimes);
iintent.putExtra("goodTimes",GameActivity.goodTimes);
iintent.putExtra("missTimes",GameActivity.missTimes);
TimerTask task = new TimerTask(){
public void run(){
mcontext.startActivity(iintent);
}
};
Timer timer = new Timer();
timer.schedule(task, 2000);
}
}
}
@Override
public void onAnimationCancel(Animator animation) {
}
@Override
public void onAnimationRepeat(Animator animation) {
}
});
animator.start();//開始動畫
}
}