|
@@ -1,20 +1,32 @@
|
|
|
package com.xunao.effectdemo.activity;
|
|
|
|
|
|
+import static com.blankj.utilcode.util.ScreenUtils.getScreenHeight;
|
|
|
+import static com.blankj.utilcode.util.ScreenUtils.getScreenWidth;
|
|
|
+
|
|
|
import androidx.appcompat.app.AppCompatActivity;
|
|
|
import androidx.core.content.res.ResourcesCompat;
|
|
|
|
|
|
+import android.animation.Animator;
|
|
|
+import android.animation.AnimatorSet;
|
|
|
+import android.animation.ObjectAnimator;
|
|
|
import android.app.Activity;
|
|
|
import android.content.ClipData;
|
|
|
+import android.content.Context;
|
|
|
import android.content.Intent;
|
|
|
+import android.graphics.Color;
|
|
|
import android.os.Bundle;
|
|
|
import android.os.SystemClock;
|
|
|
import android.util.Log;
|
|
|
+import android.util.TypedValue;
|
|
|
import android.view.DragEvent;
|
|
|
+import android.view.Gravity;
|
|
|
import android.view.HapticFeedbackConstants;
|
|
|
import android.view.MotionEvent;
|
|
|
import android.view.View;
|
|
|
import android.view.ViewGroup;
|
|
|
+import android.view.ViewTreeObserver;
|
|
|
import android.view.animation.Animation;
|
|
|
+import android.view.animation.AnimationUtils;
|
|
|
import android.view.animation.TranslateAnimation;
|
|
|
import android.widget.FrameLayout;
|
|
|
import android.widget.ImageView;
|
|
@@ -24,13 +36,46 @@ import android.widget.TextView;
|
|
|
|
|
|
import com.xunao.effectdemo.R;
|
|
|
|
|
|
+import java.util.ArrayList;
|
|
|
+import java.util.List;
|
|
|
+import java.util.Random;
|
|
|
+import java.util.Timer;
|
|
|
+import java.util.TimerTask;
|
|
|
+
|
|
|
public class DragActivity extends Activity{
|
|
|
|
|
|
private static final String RED = "RED";
|
|
|
|
|
|
+ RelativeLayout select_rl;
|
|
|
+ RelativeLayout rl_bg;
|
|
|
+ TextView tv_content;
|
|
|
+
|
|
|
+ //进行滑动的TextView
|
|
|
+ private TextView moveTextView;
|
|
|
+ List<String> selectList;
|
|
|
+ private int answerSize = 0;
|
|
|
+ private int halfAnswerSize = 0;
|
|
|
+ private int selectRootWidth = 0;
|
|
|
+ private int selectRootHeight = 0;
|
|
|
+ //选项位置的集合。初始的显示位置。用于后续拖动松手后,选项做回归动画
|
|
|
+ private List<String> selectInitialLocationList;
|
|
|
+ //分隔符
|
|
|
+ private String SeparateSymbol = "-";
|
|
|
+ //认为的最小滑动值。超过这个值,就认为是滑动;如果没有超过,就是认为是点击
|
|
|
+ private int reputeMixMoveValue = 10;
|
|
|
+ //按下时候的x,y坐标
|
|
|
+ private float downX = 0;
|
|
|
+ private float downY = 0;
|
|
|
+ //当前点击或者按住了哪个选项
|
|
|
+ private int currentOptionPosition = -1;
|
|
|
+ //移动、滑动时候,当前位置的x,y坐标
|
|
|
+ private float moveCurrentX;
|
|
|
+ private float moveCurrentY;
|
|
|
+
|
|
|
private String selectedContent = "";
|
|
|
private final String correctAnswer = "选项2";
|
|
|
private int wrongNum = 0;
|
|
|
+ Timer timer = new Timer();
|
|
|
|
|
|
|
|
|
@Override
|
|
@@ -38,74 +83,304 @@ public class DragActivity extends Activity{
|
|
|
super.onCreate(savedInstanceState);
|
|
|
setContentView(R.layout.activity_drag);
|
|
|
|
|
|
- TextView ll_red = findViewById(R.id.ll_red);
|
|
|
- TextView tv1 = findViewById(R.id.tv_1);
|
|
|
- TextView tv2 = findViewById(R.id.tv_2);
|
|
|
-
|
|
|
- tv1.setOnLongClickListener(v -> {
|
|
|
- selectedContent = tv1.getText().toString();
|
|
|
-// Intent intent = new Intent();
|
|
|
-// ClipData clipData = ClipData.newIntent("label", intent);
|
|
|
- View.DragShadowBuilder shadowBuilder = new View.DragShadowBuilder(v);
|
|
|
- v.startDrag(null, shadowBuilder, tv1, 0);
|
|
|
- //震动反馈
|
|
|
- v.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS, HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING);
|
|
|
- return true;
|
|
|
- });
|
|
|
-
|
|
|
- tv2.setOnLongClickListener(v -> {
|
|
|
- selectedContent = tv2.getText().toString();
|
|
|
-// Intent intent = new Intent();
|
|
|
-// ClipData clipData = ClipData.newIntent("label", intent);
|
|
|
- View.DragShadowBuilder shadowBuilder = new View.DragShadowBuilder(v);
|
|
|
- v.startDrag(null, shadowBuilder, tv2, 0);
|
|
|
- //震动反馈
|
|
|
- v.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS, HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING);
|
|
|
- return true;
|
|
|
- });
|
|
|
-
|
|
|
- ll_red.setOnDragListener((v, event) -> {
|
|
|
- String simpleName = v.getClass().getSimpleName();
|
|
|
- Log.w(RED, "view name:" + simpleName);
|
|
|
-
|
|
|
- int action = event.getAction();
|
|
|
- switch (action) {
|
|
|
- case DragEvent.ACTION_DRAG_STARTED:
|
|
|
- Log.i(RED, "开始拖拽");
|
|
|
-// tv1.setVisibility(View.INVISIBLE);
|
|
|
- break;
|
|
|
- case DragEvent.ACTION_DRAG_ENDED:
|
|
|
- Log.i(RED, "结束拖拽");
|
|
|
-// tv1.setVisibility(View.VISIBLE);
|
|
|
- break;
|
|
|
- case DragEvent.ACTION_DRAG_ENTERED:
|
|
|
- Log.i(RED, "拖拽的view进入监听的view时");
|
|
|
- break;
|
|
|
- case DragEvent.ACTION_DRAG_EXITED:
|
|
|
- Log.i(RED, "拖拽的view离开监听的view时");
|
|
|
- break;
|
|
|
- case DragEvent.ACTION_DRAG_LOCATION:
|
|
|
- float x = event.getX();
|
|
|
- float y = event.getY();
|
|
|
- Log.i(RED, "拖拽的view在RED中的位置:x =" + x + ",y=" + y);
|
|
|
- break;
|
|
|
- case DragEvent.ACTION_DROP:
|
|
|
- if(correctAnswer.equals(selectedContent)){
|
|
|
- Log.i(RED, "释放拖拽的view");
|
|
|
- TextView localState = (TextView) event.getLocalState();
|
|
|
- ((ViewGroup) localState.getParent()).removeView(localState);
|
|
|
- ll_red.setText(selectedContent);
|
|
|
- ll_red.setBackground(ResourcesCompat.getDrawable(getResources(),R.drawable.drag_success_bg,null));
|
|
|
- }else{
|
|
|
- wrongNum = wrongNum + 1;
|
|
|
- if(wrongNum >= 2){
|
|
|
- tv2.setBackground(ResourcesCompat.getDrawable(getResources(),R.drawable.drag_hint_bg,null));
|
|
|
- }
|
|
|
+ tv_content = findViewById(R.id.tv_content);
|
|
|
+ select_rl = findViewById(R.id.select_rl);
|
|
|
+ rl_bg = findViewById(R.id.rl_bg);
|
|
|
+ initData();
|
|
|
+ }
|
|
|
+
|
|
|
+ void initData(){
|
|
|
+ selectInitialLocationList = new ArrayList<>();
|
|
|
+ // 选项集合
|
|
|
+ List<String> selectList = new ArrayList<>();
|
|
|
+ selectList.add("选项1");
|
|
|
+ selectList.add("选项2");
|
|
|
+ selectList.add("选项3");
|
|
|
+ this.selectList = selectList;
|
|
|
+
|
|
|
+ answerSize = dp2px(50);
|
|
|
+ halfAnswerSize = answerSize / 2;
|
|
|
+
|
|
|
+ ViewTreeObserver.OnGlobalLayoutListener listener = new ViewTreeObserver.OnGlobalLayoutListener() {
|
|
|
+ @Override
|
|
|
+ public void onGlobalLayout() {
|
|
|
+
|
|
|
+ View view = new View(DragActivity.this);
|
|
|
+ RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
|
|
|
+ params.width = getScreenWidth()/2;
|
|
|
+ params.height = getScreenHeight();
|
|
|
+ view.setLayoutParams(params);
|
|
|
+ view.setBackgroundColor(getResources().getColor(R.color.textGrayColor));
|
|
|
+ rl_bg.addView(view);
|
|
|
+
|
|
|
+ select_rl.getViewTreeObserver().removeOnGlobalLayoutListener(this);
|
|
|
+
|
|
|
+ handleSelect();
|
|
|
+
|
|
|
+ }
|
|
|
+ };
|
|
|
+ select_rl.getViewTreeObserver().addOnGlobalLayoutListener(listener);
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public boolean onTouchEvent(MotionEvent event) {
|
|
|
+ switch (event.getAction()){
|
|
|
+ case MotionEvent.ACTION_DOWN:
|
|
|
+ if (moveTextView != null) {
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ currentOptionPosition = -1;
|
|
|
+ downX = event.getX();
|
|
|
+ downY = event.getY();
|
|
|
+ //指定当前滑动的TextView
|
|
|
+ for (int i = 0; i < selectInitialLocationList.size(); i++) {
|
|
|
+
|
|
|
+ float x1 = Float.parseFloat(selectInitialLocationList.get(i).split(SeparateSymbol)[0]);
|
|
|
+ float x2 = x1 + answerSize*4;
|
|
|
+
|
|
|
+ float y1 = Float.parseFloat(selectInitialLocationList.get(i).split(SeparateSymbol)[1]);
|
|
|
+ float y2 = y1 + answerSize;
|
|
|
+
|
|
|
+ //x1、y1,是选项左上角的坐标。x2、y2,是选项右下角的坐标
|
|
|
+
|
|
|
+ if (downX > x1 && downX < x2 && downY > y1 && downY < y2) {
|
|
|
+ if(select_rl.getChildAt(i).getVisibility() == View.VISIBLE){
|
|
|
+ //拿到当前选择的这个选项的view
|
|
|
+ moveTextView = (TextView) select_rl.getChildAt(i);
|
|
|
+ //记下现在选的,是第几个选项
|
|
|
+ currentOptionPosition = i;
|
|
|
+ Log.e("按下", currentOptionPosition + "");
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ case MotionEvent.ACTION_MOVE:
|
|
|
+ if (moveTextView == null) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ moveCurrentX = event.getX();
|
|
|
+ moveCurrentY = event.getY();
|
|
|
+
|
|
|
+ if (Math.abs(moveCurrentX - downX) > reputeMixMoveValue && Math.abs(moveCurrentY - downY) > reputeMixMoveValue) {
|
|
|
+
|
|
|
+ //这里,按下的坐标,要减去选项的一半,让选项的中间,跟着手指动。否则,就是选项的左上角,跟着手指移动,不美观
|
|
|
+
|
|
|
+ moveTextView.setX(moveCurrentX - halfAnswerSize);
|
|
|
+ moveTextView.setY(moveCurrentY - halfAnswerSize);
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ case MotionEvent.ACTION_UP:
|
|
|
+ case MotionEvent.ACTION_CANCEL:
|
|
|
+ if (moveTextView == null) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ // 当移动到答案框内时
|
|
|
+ if(moveCurrentX > tv_content.getX() && moveCurrentX < tv_content.getX() + tv_content.getWidth() &&
|
|
|
+ moveCurrentY >tv_content.getY() && moveCurrentY < tv_content.getY() + tv_content.getHeight()){
|
|
|
+ // 正确答案
|
|
|
+ if(selectList.get(currentOptionPosition).equals(correctAnswer)){
|
|
|
+ tv_content.setText(selectList.get(currentOptionPosition));
|
|
|
+ tv_content.setBackground(ResourcesCompat.getDrawable(getResources(),R.drawable.drag_success_bg, null));
|
|
|
+ moveTextView = null;
|
|
|
+ select_rl.getChildAt(currentOptionPosition).setVisibility(View.INVISIBLE);
|
|
|
+ }else{ // 错误答案
|
|
|
+ // 先抖一抖再回去
|
|
|
+ wrongAnimator();
|
|
|
}
|
|
|
- break;
|
|
|
+ }else {
|
|
|
+ //说明没有放到答案框,就做回归动画
|
|
|
+ moveAnimator(0, 0, 0, 0, 0, -1);
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ //处理选项。计算选项,摆放选项的位置
|
|
|
+ private void handleSelect() {
|
|
|
+
|
|
|
+ selectRootWidth = select_rl.getWidth();
|
|
|
+ selectRootHeight = select_rl.getHeight();
|
|
|
+
|
|
|
+ //移除之前的子控件(避免数据造成冲突)
|
|
|
+ select_rl.removeAllViews();
|
|
|
+
|
|
|
+ float x = 0;
|
|
|
+ float y = 0;
|
|
|
+
|
|
|
+ for (int i = 0; i < selectList.size(); i++) {
|
|
|
+
|
|
|
+ String option = selectList.get(i);
|
|
|
+
|
|
|
+ TextView tvAnswer = createSelectTv(option);
|
|
|
+
|
|
|
+ x = getScreenWidth()*3/5;
|
|
|
+ y = getScreenHeight()/4 + dp2px(80) * i;
|
|
|
+
|
|
|
+ //这里的setX、setY,是指选项(正方形)左上角的坐标位置
|
|
|
+ tvAnswer.setX(x);
|
|
|
+ tvAnswer.setY(y);
|
|
|
+
|
|
|
+ //保存这个选项的坐标位置
|
|
|
+ selectInitialLocationList.add(x + SeparateSymbol + y);
|
|
|
+
|
|
|
+ select_rl.addView(tvAnswer);
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 创建选项TextView
|
|
|
+ *
|
|
|
+ * @param str textView上要展示的内容
|
|
|
+ * @return
|
|
|
+ */
|
|
|
+ private TextView createSelectTv(String str) {
|
|
|
+
|
|
|
+ TextView tv = new TextView(this);
|
|
|
+
|
|
|
+ tv.setBackground(ResourcesCompat.getDrawable(getResources(),R.drawable.drag_default_bg,null));
|
|
|
+
|
|
|
+ tv.setText(str);
|
|
|
+
|
|
|
+ tv.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 15);
|
|
|
+
|
|
|
+ RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(
|
|
|
+ ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
|
|
|
+
|
|
|
+ params.width = answerSize*4;
|
|
|
+ params.height = answerSize;
|
|
|
+ tv.setLayoutParams(params);
|
|
|
+ tv.setGravity(Gravity.CENTER);
|
|
|
+
|
|
|
+ return tv;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 抖一抖动画
|
|
|
+ private void wrongAnimator(){
|
|
|
+ wrongNum = wrongNum + 1;
|
|
|
+ Animation anim = AnimationUtils.loadAnimation(DragActivity.this, R.anim.myanim);
|
|
|
+ tv_content.setBackground(ResourcesCompat.getDrawable(getResources(), R.drawable.drag_error_bg, null));
|
|
|
+ tv_content.setText(selectList.get(currentOptionPosition));
|
|
|
+ tv_content.startAnimation(anim);
|
|
|
+ moveTextView.setVisibility(View.GONE);
|
|
|
+ //抖完后回去
|
|
|
+ timer.schedule(new TimerTask() {
|
|
|
+ @Override
|
|
|
+ public void run() {
|
|
|
+ runOnUiThread(() -> {
|
|
|
+ tv_content.setBackground(ResourcesCompat.getDrawable(getResources(), R.drawable.drag_default_bg, null));
|
|
|
+ tv_content.setText("");
|
|
|
+ moveAnimator(0, 0, 0, 0, 0, -1);
|
|
|
+ });
|
|
|
}
|
|
|
- return true;
|
|
|
- });
|
|
|
+ }, 1200);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 回归动画
|
|
|
+ * type:
|
|
|
+ * 0、没有拖动到空格处,松开手后控件回到初始位置
|
|
|
+ * 1、拖动到空格处,松开手后,隐藏控件,静默回归
|
|
|
+ * 2、点击选项,控件移动到空缺的位置
|
|
|
+ * 3、点击选项,内容填充到空格处后,控件需要静默回归
|
|
|
+ */
|
|
|
+ private void moveAnimator(final int type, float sX, float sY, float eX, float eY, final int position) {
|
|
|
+
|
|
|
+ Log.e("type is ", type + "");
|
|
|
+ Log.e("currentOptionPosition ", currentOptionPosition + "");
|
|
|
+
|
|
|
+ if (currentOptionPosition == -1 || moveTextView == null) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ moveTextView.setVisibility(View.VISIBLE);
|
|
|
+
|
|
|
+ try {
|
|
|
+
|
|
|
+ float startX = 0;
|
|
|
+ float startY = 0;
|
|
|
+ float endX = 0;
|
|
|
+ float endY = 0;
|
|
|
+
|
|
|
+ AnimatorSet animatorSet = new AnimatorSet();
|
|
|
+ ObjectAnimator translationX;
|
|
|
+ ObjectAnimator translationY;
|
|
|
+
|
|
|
+ String location = selectInitialLocationList.get(currentOptionPosition);
|
|
|
+ startX = moveTextView.getX();
|
|
|
+ startY = moveTextView.getY();
|
|
|
+
|
|
|
+ endX = Float.parseFloat(location.split(SeparateSymbol)[0]);
|
|
|
+ endY = Float.parseFloat(location.split(SeparateSymbol)[1]);
|
|
|
+
|
|
|
+ translationX = ObjectAnimator.ofFloat(
|
|
|
+ moveTextView,
|
|
|
+ "translationX",
|
|
|
+ startX,
|
|
|
+ endX
|
|
|
+ );
|
|
|
+
|
|
|
+ translationY = ObjectAnimator.ofFloat(
|
|
|
+ moveTextView,
|
|
|
+ "translationY",
|
|
|
+ startY,
|
|
|
+ endY
|
|
|
+ );
|
|
|
+
|
|
|
+ animatorSet.playTogether(translationX, translationY);
|
|
|
+
|
|
|
+ long durationTime = 1000;
|
|
|
+
|
|
|
+ animatorSet.setDuration(durationTime);
|
|
|
+
|
|
|
+ animatorSet.addListener(new Animator.AnimatorListener() {
|
|
|
+ @Override
|
|
|
+ public void onAnimationStart(Animator animator) {
|
|
|
+
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public void onAnimationEnd(Animator animator) {
|
|
|
+
|
|
|
+ //抬起手后,释放滑动的TextView
|
|
|
+ moveTextView = null;
|
|
|
+ currentOptionPosition = -1;
|
|
|
+ if(wrongNum > 2){
|
|
|
+ for(int i = 0; i < select_rl.getChildCount(); i ++){
|
|
|
+ if(((TextView) select_rl.getChildAt(i)).getText().equals(correctAnswer)){
|
|
|
+ ((TextView) select_rl.getChildAt(i)).setBackground(ResourcesCompat.getDrawable(getResources(),R.drawable.drag_hint_bg,null));
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public void onAnimationCancel(Animator animator) {
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public void onAnimationRepeat(Animator animator) {
|
|
|
+
|
|
|
+ }
|
|
|
+ });
|
|
|
+
|
|
|
+ animatorSet.start();
|
|
|
+
|
|
|
+ } catch (Exception e) {
|
|
|
+ e.printStackTrace();
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
|
|
|
+ private int dp2px(float dpValue) {
|
|
|
+ final float scale = getResources().getDisplayMetrics().density;
|
|
|
+ return (int) (dpValue * scale + 0.5f);
|
|
|
}
|
|
|
}
|