Sunday, January 13, 2019

Kéo view trong activity

Muốn kéo view di chuyển tự do toàn màn hình, ta có thể dùng service để làm. Tuy nhiên có những app ta không muốn dùng service, ta sẽ làm ngay trong activity.
Muốn di chuyển view nào đó dọc theo một layout cố định, ta làm như sau.
Tạo một file xml tên keoview trong layout như sau.
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:ads="http://schemas.android.com/apk/res-auto"
    android:layout_width="wrap_content"
    android:layout_height="fill_parent"
    android:layout_gravity="center"
    android:orientation="vertical" >
    <TextView
        android:layout_width="130dp"
        android:layout_height="30dp"
        android:layout_gravity="center"
        android:text="Đây là view kéo được"
        android:textColor="#FF0000" />
    <RelativeLayout
        android:id="@+id/root"
        android:layout_width="wrap_content"
        android:layout_height="match_parent"     android:layout_gravity="center" >
        <TextView
         android:id="@+id/te"
         android:layout_width="wrap_content"
         android:layout_height="40dp"
         android:layout_centerVertical="true"
         android:background="#2292CB"
         android:text="Chạm để kéo" />
    </RelativeLayout>
</LinearLayout>
Đây là class để kéo view
public class keo extends Activity implements View.OnTouchListener {
     private int _yDelta;
     LayoutParams params;

     @Override
     protected void onCreate(Bundle savedInstanceState) {
          super.onCreate(savedInstanceState);
          setContentView(R.layout.keoview);
          findViewById(R.id.te).setOnTouchListener(this);
          params = new WindowManager.LayoutParams(LayoutParams.WRAP_CONTENT,
                     LayoutParams.WRAP_CONTENT, LayoutParams.TYPE_PHONE,
                     LayoutParams.FLAG_NOT_FOCUSABLE, PixelFormat.TRANSLUCENT);   
}
@Override
public boolean onTouch(View view, MotionEvent event) {
final int Y = (int) event.getRawY();
switch (event.getAction() & MotionEvent.ACTION_MASK) {
          case MotionEvent.ACTION_DOWN:
               RelativeLayout.LayoutParams lParams = (RelativeLayout.LayoutParams) view
                          .getLayoutParams();
               _yDelta = Y - lParams.bottomMargin;
               break;
          case MotionEvent.ACTION_UP:
               break;
          case MotionEvent.ACTION_POINTER_DOWN:
               break;
          case MotionEvent.ACTION_POINTER_UP:
               break;
          case MotionEvent.ACTION_MOVE:
               RelativeLayout.LayoutParams layoutParams = (RelativeLayout.LayoutParams) view
                          .getLayoutParams();
               layoutParams.bottomMargin = (Y - _yDelta);
               layoutParams.topMargin = -layoutParams.bottomMargin;
               view.setLayoutParams(layoutParams);
               view.animate().translationY(Y - _yDelta).setDuration(0);
               break;
          }
     findViewById(R.id.root).invalidate();
          return true;
     }
}
Để kéo view ngang màn hình, ta sửa file keoview.xml để cho chiều ngang của Linearlayout và RelativeLayout thành
android:layout_width="match_parent"
Trong class, thêm vào một biến.
private int _xDelta;
Lệnh onTouch bây giờ như sau.
@Override
public boolean onTouch(View view, MotionEvent event) {
          final int X = (int) event.getRawX();
switch (event.getAction() & MotionEvent.ACTION_MASK) {
          case MotionEvent.ACTION_DOWN:
               RelativeLayout.LayoutParams lParams = (RelativeLayout.LayoutParams) view
                          .getLayoutParams();
               _xDelta = X - lParams.leftMargin;
              
               break;
          case MotionEvent.ACTION_UP:
               break;
          case MotionEvent.ACTION_POINTER_DOWN:
               break;
          case MotionEvent.ACTION_POINTER_UP:
               break;
          case MotionEvent.ACTION_MOVE:
               RelativeLayout.LayoutParams layoutParams = (RelativeLayout.LayoutParams) view
                          .getLayoutParams();
              
               layoutParams.leftMargin = (X - _xDelta);
               layoutParams.rightMargin = -layoutParams.leftMargin;
               view.setLayoutParams(layoutParams);
               view.animate().translationX(X - _xDelta).setDuration(0);
               break;
          }
          findViewById(R.id.root).invalidate();
          return true;
   }
Muốn kéo view tự do trên màn hình, ta kết hợp cả 2 vào.
@Override
public boolean onTouch(View view, MotionEvent event) {
     final int X = (int) event.getRawX();
     final int Y = (int) event.getRawY();
     switch (event.getAction() & MotionEvent.ACTION_MASK) {
     case MotionEvent.ACTION_DOWN:
          RelativeLayout.LayoutParams lParams = (RelativeLayout.LayoutParams) view
                          .getLayoutParams();
               _xDelta = X - lParams.leftMargin;
               _yDelta = Y - lParams.bottomMargin;
               break;
          case MotionEvent.ACTION_UP:
               dem=dem+1;
               if(dem==2){
Toast.makeText(getApplicationContext(), "Có chạm view", Toast.LENGTH_LONG).show();
               dem=0;
               }
               break;
          case MotionEvent.ACTION_POINTER_DOWN:
               break;
          case MotionEvent.ACTION_POINTER_UP:
               break;
          case MotionEvent.ACTION_MOVE:
               RelativeLayout.LayoutParams layoutParams = (RelativeLayout.LayoutParams) view
                          .getLayoutParams();
               layoutParams.bottomMargin = (Y - _yDelta);
               layoutParams.topMargin = -layoutParams.bottomMargin;
              
               layoutParams.leftMargin = (X - _xDelta);
               layoutParams.rightMargin = -layoutParams.leftMargin;
               view.setLayoutParams(layoutParams);
               view.animate().translationX(X - _xDelta).setDuration(0);
               view.animate().translationY(Y - _yDelta).setDuration(0);
               break;
          }
          findViewById(R.id.root).invalidate();
          return true;
   }
Lệnh trong case MotionEvent.ACTION_UP: là để khi chạm 2 lần vào view thì thực hiện tác vụ nào đó, ở đây là show ra thông báo có chạm. Chú ý phải khai báo thêm biến dem. Lệnh này có hạn chế là cứ kéo view 2 lần là nó show toast ra. Nên bạn có thể tham khảo cách phát hiện có chạm view ở bài kéo view nổi trên màn hình dùng service để làm cách khác ưng ý hơn.
Tại bài này cũng có hướng dẫn dùng SharedPreferences để lưu vị trí view đã di chuyển tới, lần sau mở ra thì view vẫn ở vị trí đã kéo tới chứ không phải lại về vị trí ban đầu.

Cách này có ưu điểm là khi chạy rất nhẹ, không gây crash app.

No comments:

Post a Comment