جسچرها و حرکات لمسی در اندروید

جسچرها و حرکات لمسی در اندروید

تکنولوژی موبایل یکی از مهم ترین بخش های عصر فناوری است که توسعه دهندگان نیز بر روی آن متمرکز شده اند و یکی از دلایل موفقیت موبایل قابلیت برقراری تعامل آن با کاربران است که با کمک حرکات و امکانات دیگر میسر می شود. کاربران با روش های متنوعی از حرکات تاچ ساده گرفته تا درگ کردن ویوها و حرکات چند انگشتی به تعامل با اسمارت فون های خود می پردازند. در این لینک نمونه ای از یک مجموعه کامل از حرکات موبایل پایه را مشاهده می کنید.

blog_16389_1

در این مقاله مهم ترین تعاملات کاربری و حرکات اندروید و جایگذاری حرکات کاستوم و ایونت های تاچ معرفی می شود.

ساخت یک اپلیکیشن تعاملی تنها با onClick امکان پذیر نیست و SDK اندروید از حرکات متنوعی پشتیبانی می کند. بدین ترتیب توسعه دهندگان قادر به طراحی روش های جدیدی جهت تعامل کاربران با اپلیکیشن می گردند. در اندروید از کلاس GestureDetector به منظور تشخیص ایونت های حرکتی مقدماتی و از MotionEvent برای مدیریت حرکات پیچیده تر استفاده می شود.

1. GestureDetector

کلاس GestureDetector ایونت های حرکتی را که به مجموعه خاصی از حرکات کاربر ارتباط پیدا می کنند دریافت می نماید. این حرکات شامل ضربه زدن، کشیدن افقی و عمودی، فشار کوتاه و طولانی بر روی صفحه، دوبار ضربه زدن و اسکرول ها می شود. GestureDetector در مدیریت تعاملات استاندارد کاربری عملکرد قدرتمندی دارد و ست کردن SimpleOnGestureLister در عناصر رابط کاربری اندروید به سادگی صورت می پذیرد.

این کلاس به اورراید کردن متدهایی موردنیاز از GestureLister پرداخته و بدون نیاز به تشخیص دستی حرکت صورت گرفته از سوی کاربر به اجرای آن می پردازد.

تشخیص جسچرهای متداول

مثال زیر نمونه ای از جسچرهای متداول کلاس GestureDetector می باشد. در گام نخست لازم است یک نمونه از کلاس GestureDetector را در داخل کلاس اکتیویتی خود تعریف کنید.

  private GestureDetector mGestureDetector;

در داخل کلاس اکتیویتی یک کلاس جاوا بسازید که دارای رابط های GestureDetector.OnGestureListener و GestureDetector.OnDoubleTapList به منظور تشخیص حرکات پایه اندروید باشد.

class Android_Gesture_Detector implements GestureDetector.OnGestureListener,

GestureDetector.OnDoubleTapListener {

@Override

public boolean onDown(MotionEvent e) {

Log.d("Gesture ", " onDown");

return true;

}

@Override

public boolean onSingleTapConfirmed(MotionEvent e) {

Log.d("Gesture ", " onSingleTapConfirmed");

return true;

}

@Override

public boolean onSingleTapUp(MotionEvent e) {

Log.d("Gesture ", " onSingleTapUp");

return true;

}

@Override

public void onShowPress(MotionEvent e) {

Log.d("Gesture ", " onShowPress");

}

@Override

public boolean onDoubleTap(MotionEvent e) {

Log.d("Gesture ", " onDoubleTap");

return true;

}

@Override

public boolean onDoubleTapEvent(MotionEvent e) {

Log.d("Gesture ", " onDoubleTapEvent");

return true;

}

@Override

public void onLongPress(MotionEvent e) {

Log.d("Gesture ", " onLongPress");

}

@Override

public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {

Log.d("Gesture ", " onScroll");

if (e1.getY() < e2.getY()){

Log.d("Gesture ", " Scroll Down");

}

if(e1.getY() > e2.getY()){

Log.d("Gesture ", " Scroll Up");

}

return true;

}

@Override

public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {

if (e1.getX() < e2.getX()) {

Log.d("Gesture ", "Left to Right swipe: "+ e1.getX() + " - " + e2.getX());

Log.d("Speed ", String.valueOf(velocityX) + " pixels/second");

}

if (e1.getX() > e2.getX()) {

Log.d("Gesture ", "Right to Left swipe: "+ e1.getX() + " - " + e2.getX());

Log.d("Speed ", String.valueOf(velocityX) + " pixels/second");

}

if (e1.getY() < e2.getY()) {

Log.d("Gesture ", "Up to Down swipe: " + e1.getX() + " - " + e2.getX());

Log.d("Speed ", String.valueOf(velocityY) + " pixels/second");

}

if (e1.getY() > e2.getY()) {

Log.d("Gesture ", "Down to Up swipe: " + e1.getX() + " - " + e2.getX());

Log.d("Speed ", String.valueOf(velocityY) + " pixels/second");

}

return true;

}

}

کلاس Android_Gesture_Detector به رهگیری تمامی جسچرهای مقدماتی اجرا شده درون خود می پردازد، اما به منظور استفاده از این جسچرها باید اقدام به اورراید نمودن متد ()onTouchEvent جهت رهگیری ایونت های تاچ نموده و آنها را به کلاس Android_Gesture_Detector تغییر مسیر دهید.

@Override

public boolean onTouchEvent(MotionEvent event) {

mGestureDetector.onTouchEvent(event);

return super.onTouchEvent(event);

// Return true if you have consumed the event, false if you haven't.

// The default implementation always returns false.

}

قسمت زیر را به متد ()onCreate بیفزایید، با این کار gesture detector اندروید قادر به تشخیص تعاملات کاربر می گردد.

// Create an object of the Android_Gesture_Detector  Class

Android_Gesture_Detector android_gesture_detector = new Android_Gesture_Detector();

// Create a GestureDetector

mGestureDetector = new GestureDetector(this, android_gesture_detector);

تست کردن کلاس gesture

اپلیکیشن را به اجرا در آورده و با آن کار کنید و نتیجه را که در پنجره Android Studio LogCat به نمایش در آمده مشاهده کنید.

blog_16389_2

پس از این مرحله می توانید به ساخت عملکرد اپلیکیشن درون متدهای اورراید شده بپردازید.

Pinch Gesture

قابلیت تغییر اندازه عناصر رابط کاربری در صفحه یکی دیگر از حرکات کاربردی در هر اپلیکیشن می باشد که با کمک کلاس ScaleGestureDetector جهت تغییر اندازه ویوها قادر به استفاده از این ویژگی می باشیم. در کد زیر از کلاس ScaleListener جهت اجرای حرکت pinch در imageView استفاده شده و می توان با حرکت دادن انگشت عناصر صفحه را کوچک و بزرگ کرد.

import android.app.Activity;

import android.os.Bundle;

import android.view.MotionEvent;

import android.view.ScaleGestureDetector;

import android.widget.ImageView;

import android.widget.Toast;

public class MainActivity extends Activity {

private ImageView imageView;

private float scale = 1f;

private ScaleGestureDetector detector;

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

imageView=(ImageView)findViewById(R.id.imageView);

detector = new ScaleGestureDetector(this,new ScaleListener());

}

public boolean onTouchEvent(MotionEvent event) {

// re-route the Touch Events to the ScaleListener class

detector.onTouchEvent(event);

return super.onTouchEvent(event);

}

private class ScaleListener extends ScaleGestureDetector.SimpleOnScaleGestureListener {

float onScaleBegin = 0;

float onScaleEnd = 0;

@Override

public boolean onScale(ScaleGestureDetector detector) {

scale *= detector.getScaleFactor();

imageView.setScaleX(scale);

imageView.setScaleY(scale);

return true;

}

@Override

public boolean onScaleBegin(ScaleGestureDetector detector) {

Toast.makeText(getApplicationContext(),"Scale Begin" ,Toast.LENGTH_SHORT).show();

onScaleBegin = scale;

return true;

}

@Override

public void onScaleEnd(ScaleGestureDetector detector) {

Toast.makeText(getApplicationContext(),"Scale Ended",Toast.LENGTH_SHORT).show();

onScaleEnd = scale;

if (onScaleEnd > onScaleBegin){

Toast.makeText(getApplicationContext(),"Scaled Up by a factor of " + String.valueOf( onScaleEnd / onScaleBegin ), Toast.LENGTH_SHORT ).show();

}

if (onScaleEnd < onScaleBegin){

Toast.makeText(getApplicationContext(),"Scaled Down by a factor of " + String.valueOf( onScaleBegin / onScaleEnd ), Toast.LENGTH_SHORT ).show();

}

super.onScaleEnd(detector);

}

}

}

کلاس ScaleListener سه متد onScale ، onScaleBegin و onScaleEnd را اورراید می کند. در حین اجرای متد onScale تغییر اندازه iamgeView بر پایه مقیاسی از حرکت انگشت بر روی صفحه نمایش گوشی به اجرا در می آید.

blog_16389_3

2. Motion Event

Android Simple GestureDetector برای حرکات ساده و مقدماتی بسیار کاربردی است، اما برای حرکاتی که شامل دو یا تعداد بیشتری تاچ هستند به متد دیگری نیاز دارید. لزومی به استفاده از ایونت های تاچ در تمامی اپلیکیشن ها احساس نمی شود اما در صورت تمایل به ساخت یک اپلیکیشن کاملا تعاملی استفاده از این کلاس تنها راه حل است. وظیفه اصلی کلاس MotionEvent گرفتن ایونت های تاچ در یک اکتیویتی و اورراید کردن فراخوانی ()onTouchEvent است و در ویوها با استفاده از متد ()setOnTouchListener که برای گوش دادن به ایونت های تاچ در ویو استفاده می شود کار خود را به انجام می رساند. همانطور که در مثال زیر نیز نمایش داده شده می توان با اجرای View.onTouachListener در کلاس اکتیویتی ایونت های تاچ را در ویو گرفت.

اندروید به تولید ایونت های تاچ زیر در هر جایی که صفحه با یک یا چند انگشت لمس شود می پردازد.

ACTION_DOWN

برای نخستین اشاره گری که صفحه را لمس می کند تاچ جدید شروع می شود.

ACTION_MOVE

یک تغییر در حرکت تاچ صورت گرفته و  انگشت در حال حرکت است.

ACTION_UP

آخرین اشاره گر صفحه را ترک می کند.

ACTION_POINTER_DOWN

برای اشاره گرهای اضافه ای که پس از اشاره گر اول وارد صفحه می شوند یعنی مولتی تاچ.

ACTION_POINTER_UP

زمانی که یک اشاره گر غیراولیه بالا می آید (مولتی تاچ).

ACTION_CANCEL

ایونت تاچ کنسل شده و چیز دیگری کنترل ایونت را بر عهده می گیرد.

جهت نمایش کاربرد کلاس MotionEvent کار را با مثالی ادامه می دهیم. حرکات به منظور تعامل با رابط کاربری اپلیکیشن اند، بنابراین به شرح مثالی از یک اپلیکیشن که ویوها را در لی اوت اصلی با کمک یک حرکت انگشت جابجا می کند، اندازه ویو را با کمک حرکت دو انگشت بزرگ می کند و به کمک سه انگشت می چرخاند می پردازیم.

بیایید کار را با این ایونت های تاچ آغاز نماییم، یک اکتیویتی اندروید بسازید که دارای View.OntouchListener باشد. آی دی rootLayout را در فایل اکتیویتی به لی اوت Relative اصلی ست کنید. هدف افزودن عناصر imageView در لی اوت روت می باشد که هرکدام دارای قابلیت عملکرد بر اساس یک ایونت تاچ هستند. جهت افزودن imageViewهای جدید بر روی لی اوت دکمه ای را که با هر بار کلیک تابع ()Add_Image را فراخوانی می کند افزوده ایم.

این تابع یک imageView می سازد و یک منبع برای آن ست می کند، یک مجموعه از پارامترهای لی اوت را می سازد و آنها را به imageView متصل می نماید، سپس این imageView را به لی اوت روت اضافه کرده و Touch Listener را به این ویو ست می کند.

private void Add_Image() {

final ImageView iv = new ImageView(this);

iv.setImageResource(R.drawable.image);

RelativeLayout.LayoutParams layoutParams = new RelativeLayout.LayoutParams(150, 150);

iv.setLayoutParams(layoutParams);

RootLayout.addView(iv, layoutParams);

iv.setOnTouchListener(this);

}

مهم ترین بخش TouchListener ویو است. این لیسنر اکشن ها را بر اساس ایونت های تاچی که در بالا شرح داده شد به اجرا در می آورد.

در حین اجرای ایونت ACTION_DOWN مکان های X و Y را دریافت می کنیم و حاشیه های مربوطه را حذف می نماییم تا به نقطه دقیق در ویو که تاچ بر روی آن صورت گرفته برسیم.

در حین اجرای ایونت ACTION_UP یک ایونت دابل کلیک کاستوم جهت پاک کردن ویویی که این ایونت بر روی آن انجام شده اجرا شده است.

در حین ACTION_MOVE اکشن هایی برای امکانات جالبی که در بالا شرح داده شد به اجرا در آمده است. یکی از ویژگی های جالب درباره کلاس MotionEvent قابلیت تشخیص تعداد انگشت هایی است که صفحه را لمس کرده اند و برای این کار از متد ()getPointerCount استفاده شده است.

یک انگشت – جابجایی ویو

LayoutParams مربوط به ویو را بگیرید و آن را بر اساس حرکت انگشت بر روی صفحه ریست کنید. حاشیه سمت چپ برابر با مکان ایونت تاچ منهای مقدار X از تاچ درون ویو که در حین ACTION_DOWN ست شده است می باشد، بنابراین ویو بر اساس مقدار X از انگشت که بر روی لی اوت قرار گرفته جابجا می شود و خود را بر اساس مکان تاچ درون ویو تراز می کند.

همین منطق برای حاشیه بالایی نیز صدق می کند، اما بر اساس محور Y.  مقدار 500 برای حاشیه های راست و پایین ست شده است، بنابراین لمس حاشیه های راست و پایین لی اوت اندازه را کم نمی کند و تنها موجب پنهان شدن آن قسمت می گردد.

RelativeLayout.LayoutParams Params = (RelativeLayout.LayoutParams) view.getLayoutParams();

Params.leftMargin = X - Position_X;

Params.topMargin = Y - Position_Y;

Params.rightMargin = -500;

Params.bottomMargin = -500;

view.setLayoutParams(Params);

دو انگشت – بزرگ کردن ویو

با گرفتن LayoutParams ویو عرض و طول آن را تغییر دهید. در حین عملیات تغییر اندازه حاشیه های ویو تغییر پیدا نمی کنند و عملیات تغییر اندازه بر اساس حرکت انگشت اشاره صورت می پذیرد، بنابراین اندازه ها با توجه به مکان قرارگیری انگشت اشاره بر روی لی اوت ست می شوند. مقادیر ثابت Position_X و Position_Y تاثیری بر روی عملیات تغییر اندازه ندارند و تنها برای موارد دیداری مورد استفاده قرار می گیرند.

RelativeLayout.LayoutParams layoutParams1 =  (RelativeLayout.LayoutParams) view.getLayoutParams();

layoutParams1.width = Position_X +(int)event.getX();

layoutParams1.height = Position_Y + (int)event.getY();

view.setLayoutParams(layoutParams1);

سه انگشت – چرخش ویو

چرخش ویو به سادگی صورت می پذیرد. چرخش فعلی ویو را بگیرید و یک مقدار ثابت از نوع شناور را به آن بیفزایید و در حین ACTION_MOVE با حرکت سه انگشت با استفاده از متد ()setRotation مجددا آن را برای ویو ست کنید.

view.setRotation(view.getRotation() + 10.0f);

به خاطر داشته باشید که عملیات تغییر اندازه ویو پس از ایونت چرخش به درستی کار نخواهد کرد زیرا محورهای ویو به وضعیت چرخشی کنونی چرخانده شده اند.

کد کامل برای اکتیویتی MotionEvent به صورت زیر می باشد:

import android.app.Activity;

import android.app.AlertDialog;

import android.content.DialogInterface;

import android.os.Bundle;

import android.view.MotionEvent;

import android.view.View;

import android.view.ViewGroup;

import android.widget.Button;

import android.widget.ImageView;

import android.widget.RelativeLayout;

public class MainActivity extends Activity implements View.OnTouchListener {

int clickCount;

private ViewGroup RootLayout;

private int Position_X;

private int Position_Y;

long startTime = 0 ;

@Override

public void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

RootLayout = (ViewGroup) findViewById(R.id.rootLayout);

//new image

Button NewImage = (Button)findViewById(R.id.new_image_button);

NewImage.setOnClickListener(new View.OnClickListener() {

@Override

public void onClick(View v) {

Add_Image();

}

});

clickCount = 0;

}

private void Add_Image() {

final ImageView iv = new ImageView(this);

iv.setImageResource(R.drawable.image);

RelativeLayout.LayoutParams layoutParams = new RelativeLayout.LayoutParams(150, 150);

iv.setLayoutParams(layoutParams);

RootLayout.addView(iv, layoutParams);

iv.setOnTouchListener(this);

}

public boolean onTouch(final View view, MotionEvent event) {

final int X = (int) event.getRawX();

final int Y = (int) event.getRawY();

int pointerCount = event.getPointerCount();

switch (event.getAction() & MotionEvent.ACTION_MASK) {

case MotionEvent.ACTION_DOWN:

RelativeLayout.LayoutParams layoutParams = (RelativeLayout.LayoutParams) view.getLayoutParams();

Position_X = X - layoutParams.leftMargin;

Position_Y = Y - layoutParams.topMargin;

break;

case MotionEvent.ACTION_UP:

if (startTime == 0){

startTime = System.currentTimeMillis();

}else {

if (System.currentTimeMillis() - startTime < 200) {

AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this);

builder.setMessage("Are you sure you want to delete this?");

builder.setPositiveButton("Yes", new DialogInterface.OnClickListener() {

@Override

public void onClick(DialogInterface dialog, int which) {

view.setVisibility(View.GONE);

}

});

builder.setNegativeButton("No", new DialogInterface.OnClickListener() {

@Override

public void onClick(DialogInterface dialog, int which) {

dialog.dismiss();

}

});

AlertDialog alertDialog = builder.create();

alertDialog.show();

}

startTime = System.currentTimeMillis();

}

break;

case MotionEvent.ACTION_POINTER_DOWN:

break;

case MotionEvent.ACTION_POINTER_UP:

break;

case MotionEvent.ACTION_MOVE:

if (pointerCount == 1){

RelativeLayout.LayoutParams Params = (RelativeLayout.LayoutParams) view.getLayoutParams();

Params.leftMargin = X - Position_X;

Params.topMargin = Y - Position_Y;

Params.rightMargin = -500;

Params.bottomMargin = -500;

view.setLayoutParams(Params);

}

if (pointerCount == 2){

RelativeLayout.LayoutParams layoutParams1 = (RelativeLayout.LayoutParams) view.getLayoutParams();

layoutParams1.width = Position_X +(int)event.getX();

layoutParams1.height = Position_Y + (int)event.getY();

view.setLayoutParams(layoutParams1);

}

//Rotation

if (pointerCount == 3){

//Rotate the ImageView

view.setRotation(view.getRotation() + 10.0f);

}

break;

}

// Schedules a repaint for the root Layout.

RootLayout.invalidate();

return true;

}

}

تست اپلیکیشن MotionEvent

ویوی جابجایی

blog_16389_4

ویوی تغییر اندازه

blog_16389_5

ویوی چرخش

blog_16389_6

جمع بندی

در این مقاله دو نوع اصلی از کلاس های جسچر در اندروید شرح داده شد و با کمک دانش مقدماتی که در اختیار شما قرار گرفت می توانید با این جسچرها کار کنید و به ارتقای تجربه کاربری اپلیکیشن خود بپردازید.

 

http://www.sitepoint.com برگرفته از

اینها را هم بخوانید

نظرات کاربران

  1. محمد رضا داودآبادی
    محمد رضا داودآبادی
    روت لایوت چیه

    به این نظر پاسخ دهید
    1. محمد رضا داودآبادی
      محمد رضا داودآبادی
      ببخشید اشتباه شد واقعاً آموزش خوبی بود من یک ماه داشتم دنبال همچین چیزی می گشتم تشکّر سلام!

      به این نظر پاسخ دهید
  2. avatar
    مهسا
    عااااااااااااالی بووووود:)))))))))))

    به این نظر پاسخ دهید