我想做一个简单的控件:一个里面有视图的容器。如果我触摸容器并移动手指,我想让视图跟着我的手指移动。

我应该使用什么样的容器(布局)?如何做到这一点?

我不需要使用一个表面,但一个简单的布局。


当前回答

触摸容器,视图将跟随您的手指。

xml代码

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    android:id="@+id/floating_layout"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    >

    <ImageView
      android:id="@+id/btn_chat"
      android:layout_width="42dp"
      android:layout_height="42dp"
      />
    
<LinearLayout>

Java代码

public class DashBoardActivity extends Activity implements View.OnClickListener, View.OnTouchListener {
    
    float dX;
    float dY;
    int lastAction;
    LinearLayout floatingLayout;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_dashboard);

        floatingLayout = findViewById(R.id.floating_layout);
        floatingLayout.setOnTouchListener(this);    
    
    
    
     @Override
    public boolean onTouch(View view, MotionEvent event) {
        switch (event.getActionMasked()) {
            case MotionEvent.ACTION_DOWN:
                dX = view.getX() - event.getRawX();
                dY = view.getY() - event.getRawY();
                lastAction = MotionEvent.ACTION_DOWN;
                break;

            case MotionEvent.ACTION_MOVE:
                view.setY(event.getRawY() + dY);
                view.setX(event.getRawX() + dX);
                lastAction = MotionEvent.ACTION_MOVE;
                break;

            case MotionEvent.ACTION_UP:
                if (lastAction == MotionEvent.ACTION_DOWN)
                    Toast.makeText(DashBoardActivity.this, "Clicked!", Toast.LENGTH_SHORT).show();
                break;

            default:
                return false;
        }
        return true;
    }
}

其他回答

创建一个自定义的触摸监听器类(在Kotlin中):

(这段代码限制了视图从父视图中拖出)

class CustomTouchListener(
  val screenWidth: Int, 
  val screenHeight: Int
) : View.OnTouchListener {
    private var dX: Float = 0f
    private var dY: Float = 0f

    override fun onTouch(view: View, event: MotionEvent): Boolean {

        val newX: Float
        val newY: Float

        when (event.action) {
            MotionEvent.ACTION_DOWN -> {
                dX = view.x - event.rawX
                dY = view.y - event.rawY
            }
            MotionEvent.ACTION_MOVE -> {

                newX = event.rawX + dX
                newY = event.rawY + dY

                if ((newX <= 0 || newX >= screenWidth - view.width) || (newY <= 0 || newY >= screenHeight - view.height)) {
                    return true
                }

                view.animate()
                    .x(newX)
                    .y(newY)
                    .setDuration(0)
                    .start()
            }
        }
        return true
    }
}

如何使用它?

parentView.viewTreeObserver.addOnGlobalLayoutListener { view.setOnTouchListener(CustomTouchListener(parentView.width, parentView.height)) }

parentView是视图的父视图。

我发现了一个简单的方法来做到这一点与ViewPropertyAnimator:

float dX, dY;

@Override
public boolean onTouch(View view, MotionEvent event) {

    switch (event.getAction()) {

        case MotionEvent.ACTION_DOWN:

            dX = view.getX() - event.getRawX();
            dY = view.getY() - event.getRawY();
            break;

        case MotionEvent.ACTION_MOVE:

            view.animate()
                    .x(event.getRawX() + dX)
                    .y(event.getRawY() + dY)
                    .setDuration(0)
                    .start();
            break;
        default:
            return false;
    }
    return true;
}

//如果你想移动你的相机或任何东西,然后按照下面的方法来做 //我在相机上实现的情况下,你可以应用它在任何你想要的

public class VideoCallActivity extends AppCompatActivity implements 
  View.OnTouchListener {
 FrameLayout myLayout1;

@SuppressLint("ClickableViewAccessibility")
@Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
  //in the frame layout I am setting my camera
   myLayout1.setOnTouchListener(this);
  
  }

   float dX, dY;

@Override
public boolean onTouch(View view, MotionEvent event) {
    switch (event.getAction()) {
 //this is your code
        case MotionEvent.ACTION_DOWN:
            dX = view.getX() - event.getRawX();
            dY = view.getY() - event.getRawY();
            break;
        case MotionEvent.ACTION_MOVE:
            view.animate()
                    .x(event.getRawX() + dX)
                    .y(event.getRawY() + dY)
                    .setDuration(0)
                    .start();
            break;
        default:
            return false;
    }
    return true;
}

在Kotlin中实现相同

    rightPanel.setOnTouchListener(View.OnTouchListener { view, event ->
        when (event?.action) {
            MotionEvent.ACTION_DOWN -> {

                rightDX = view!!.x - event.rawX
                // rightDY = view!!.getY() - event.rawY;

            }
            MotionEvent.ACTION_MOVE -> {

                var displacement = event.rawX + rightDX

                view!!.animate()
                        .x(displacement)
                        // .y(event.getRawY() + rightDY)
                        .setDuration(0)
                        .start()
            }
            else -> { // Note the block
                return@OnTouchListener false
            }
        }
        true
 })

触摸容器,视图将跟随您的手指。

xml代码

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    android:id="@+id/floating_layout"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    >

    <ImageView
      android:id="@+id/btn_chat"
      android:layout_width="42dp"
      android:layout_height="42dp"
      />
    
<LinearLayout>

Java代码

public class DashBoardActivity extends Activity implements View.OnClickListener, View.OnTouchListener {
    
    float dX;
    float dY;
    int lastAction;
    LinearLayout floatingLayout;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_dashboard);

        floatingLayout = findViewById(R.id.floating_layout);
        floatingLayout.setOnTouchListener(this);    
    
    
    
     @Override
    public boolean onTouch(View view, MotionEvent event) {
        switch (event.getActionMasked()) {
            case MotionEvent.ACTION_DOWN:
                dX = view.getX() - event.getRawX();
                dY = view.getY() - event.getRawY();
                lastAction = MotionEvent.ACTION_DOWN;
                break;

            case MotionEvent.ACTION_MOVE:
                view.setY(event.getRawY() + dY);
                view.setX(event.getRawX() + dX);
                lastAction = MotionEvent.ACTION_MOVE;
                break;

            case MotionEvent.ACTION_UP:
                if (lastAction == MotionEvent.ACTION_DOWN)
                    Toast.makeText(DashBoardActivity.this, "Clicked!", Toast.LENGTH_SHORT).show();
                break;

            default:
                return false;
        }
        return true;
    }
}