編輯:Android開發實例
觸摸屏做出動作已經成為主流手機的操作規范,但是你想過怎麼開發觸摸屏手機的手勢操作嗎?本文將向各位介紹Android觸摸屏手機開發的textview及listview對比驗證。
View可以通過onTouchEvent收到觸摸屏幕事件
我們可以通過View.setOnTouchListener()設置事件監聽器
或者override onTouchEvent()來攔截這些事件
在攔截函數中判斷觸摸點的軌跡及運動速度就可以判斷出是什麼手勢。
Android系統提供了GestureDetector來方便手勢的判斷,即:在攔截函數中每次touchevent都把事件作為參數調用GestureDetector.onTouchEvent(),當有手勢被識別出後,就會通知調用者。
為了能通知到調用者,GestureDetector在構造時要求傳入一個實現了OnGestureListener接口的對象,通過此對象就可以接收到各種手勢通知了。
首先,用textview來實驗:
用一個ViewFlipper放置兩個Textview,當手指在屏幕上左右移動時進行進行切換。
由於textview本身不會處理touchevent,而是會將其繼續上傳,所以textview布局時layout_width及layout_height沒有影響。
如果touch事件發生在textview上會繼續上傳到viewflipper;如果發生在viewflipper上那麼就直接處理。
所以我們應該攔截發生在ViewFlipper上的touchEvent,並進行處理。
代碼如下,在onCreate中完成界面布局及事件攔截函數設置:
Java代碼
- //創建一個ViewFlipper
- mVf = new ViewFlipper(this);
- //ViewFlipper打開長點擊支持。如果不打開,無法收到長時間點擊,那麼手勢判斷就無從進行。
- mVf.setLongClickable(true);
- //攔截ViewFlipper的touch事件,並使用GestureDetector.onTouchEvent來處理
- mVf.setOnTouchListener(new OnTouchListener() {
- @Override
- public boolean onTouch(View v, MotionEvent event) {
- return mVfDetector.onTouchEvent(event);
- }
- });
- //添加一個textview。textview不能setLongClickable(true),
- //如果設置了那麼發生在textview上的touch事件就無法傳給viewflipper,
- //無法被手勢識別對象處理了。
- TextView tv = new TextView(this);
- tv.setText("TextView 1");
- tv.setBackgroundColor(0xffffffff); //設置一個白色背景,方便看到textview區域
- mVf.addView(tv,new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT));
- //再添加一個textview
- TextView tv2 = new TextView(this);
- tv2.setText("TextView 2");
- tv2.setBackgroundColor(0xffffffff);
- mVf.addView(tv2,new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));
- //將viewFlipper作為Activity的主view顯示
- setContentView(mVf);
在監聽器中用到了GestureDetector對象,其是一個類成員對象,用如下代碼創建:
Java代碼
- private GestureDetector mVfDetector = new GestureDetector(new OnGestureListener() {
- //手指在屏幕上移動距離小於此值不會被認為是手勢
- private static final int SWIPE_MIN_DISTANCE = 120;
- //手指在屏幕上移動速度小於此值不會被認為手勢
- private static final int SWIPE_THRESHOLD_VELOCITY = 200;
- //手勢識別函數,到此函數被系統回調時說明系統認為發生了手勢事件,
- //我們可以做進一步判定。
- @Override
- public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
- //如果第1個坐標點大於第二個坐標點,說明是向左滑動
- //滑動距離以及滑動速度是額外判斷,可根據實際情況修改。
- if(e1.getX() - e2.getX() > SWIPE_MIN_DISTANCE &&
- Math.abs(velocityX) > SWIPE_THRESHOLD_VELOCITY) {
- //left
- Log.i("GestureDemo", "ViewFlipper left");
- mVf.showNext();
- return true;
- }else if (e2.getX() - e1.getX() > SWIPE_MIN_DISTANCE &&
- Math.abs(velocityX) > SWIPE_THRESHOLD_VELOCITY) {
- //right
- Log.i("GestureDemo", "ViewFlipper right");
- mVf.showPrevious();
- return true;
- }
- return false;
- }
- ... ...
- ... ...
- });
- }
我們將前文中添加第二個textview的改為添加Listview,代碼如下:
Java代碼
- ListView lv = new ListView(this);
- lv.setBackgroundColor(0xff808080);
- final String[] items = {"one", "two", "three"};
- lv.setAdapter(new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, items));
- mVf.addView(lv,new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));
執行ap,滑動屏幕切換到第二屏,可以看到第二屏被換成了一個ListView,並且沒有填充完整個屏幕,這時如果在底部非ListView區域向左滑動,仍然可以切換到第一屏,但是在Listview區域滑動就沒有效果了,因為touch事件被Listview處理了,ViewFlipper無法收到touch事件也就無法進行手勢判斷。
我們給ListView也增加一個touch事件監聽器,代碼如下:
Java代碼
- lv.setOnTouchListener(new OnTouchListener() {
- @Override
- public boolean onTouch(View v, MotionEvent event) {
- return mVfDetector.onTouchEvent(event);
- }
- });
此時在ListView上滑動也能實現屏幕切換了。
我們再給Listview增加響應點擊事件的處理,代碼如下:
Java代碼
- lv.setOnItemClickListener(new OnItemClickListener() {
- @Override
- public void onItemClick(AdapterView> arg0, View arg1,
- int arg2, long arg3) {
- new AlertDialog.Builder(MainActivity.this)
- .setMessage(items[arg2])
- .create()
- .show();
- }
- });
當點擊Listview的條目的時候,就會彈出窗口顯示點擊了哪一項,此時的代碼可參見附件1。
截止到此時,似乎Listview支持左右滑動的操作完成了,但實際上仍有兩個問題:
首先就是滑動時,Listview有時會有條目被高亮,這個問題倒還不是太嚴重。
再次就是ContextMenu每次滑動都會被激活,我們可以通過代碼驗證,創建ListView的代碼稍作修改:
Java代碼
- //使Listview長一些
- final String[] items = {"one", "two", "three", "four", "five", "six", "sevent", "eight", "nine"};
- registerForContextMenu(lv);
另外Activity的代碼增加:
Java代碼
- @Override
- public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) {
- menu.add("Menu 1");
- menu.add("Menu 2");
- menu.add("Menu 3");
- super.onCreateContextMenu(menu, v, menuInfo);
- }
當我們在ListView的某一項上長按時就會彈出菜單,但是當滑動時,即使屏幕切換到了第一屏,此菜單仍然會彈出。
為了解決上面的問題,我嘗試了以下辦法:
1.在ListView的touch事件監聽函數中始終返回true,吃掉所有事件,這樣的修改導致不能相應點擊,ListView不能上下滑動,此路不通。
2.在GestureDetector的fling函數中向ListView發送一個MotionEvent.ACTION_CANCEL事件,總是空指針異常,懷疑是因為touch事件沒有被ListView處理過,其內部成員狀態異常,於是我從ListView繼承實現了一個ListView,在onTouchEvent中呼叫super.onTouchEvent,但是發送MotionEvent.ACTION_CANCEL事件時仍然是空指針異常,再次失敗。
3.GestureDetector的onDown函數返回true,吃掉down事件,此時點擊時沒有高亮項了,切換時contextmenu也不被trigger了,但是長按也無法彈出contextmenu了。
為了能彈出contextMenu,那麼就要在GestureDetector的onLongPress函數中調用ListView.showContextMenuForChild()來彈出菜單。
那麼GestureDetector就與ViewFlipper的不通用了。
所以我重新由ListView繼承實現了一個類,這個類自身綁定了一個GestureDetector:
Java代碼
- @Override
- public boolean onDown(MotionEvent e) {
- return true; //吃掉Down事件
- }
- @Override
- public void onLongPress(MotionEvent e) {
- System.out.println("Listview long press");
- int position = pointToPosition((int)e.getX(), (int)e.getY());
- if( position != ListView.INVALID_POSITION) {
- View child = getChildAt(position - getFirstVisiblePosition());
- if(child != null) GestureListView.this.showContextMenuForChild(child);
- }
- }
另外為了能夠顯示contextmenu時在哪一項上激活的,在Activity增加函數:
Java代碼
- @Override
- public boolean onContextItemSelected(MenuItem item) {
- AdapterContextMenuInfo info = (AdapterContextMenuInfo) item.getMenuInfo();
- System.out.println("View " + info.position + " context menu activited.");
- return super.onContextItemSelected(item);
- }
從LogCat即可看到打印輸出。
此時此ListView可以響應手勢、可以響應點擊、可以彈出菜單,基本的功能已經滿足,另外再微調一下ondown函數,當點擊時可以高亮一下。
可以顯示在的Android任務,通過加載進度條的進展。進度條有兩種形狀。加載欄和加載微調(spinner)。在本章中,我們將討論微調(spinner)。Spinner 用
登錄應用程序的屏幕,詢問憑據登錄到一些特定的應用。可能需要登錄到Facebook,微博等本章介紹了,如何創建一個登錄界面,以及如何管理安全問題和錯誤嘗試。首先,必須定義兩
Android提供了許多方法來控制播放的音頻/視頻文件和流。其中該方法是通過一類稱為MediaPlayer。Android是提供MediaPlayer類訪問內置的媒體播放
大家好,眾所周知,android裡兩個相同方向的ScrollView是不能嵌套的,那要是有這樣的需求怎麼辦?(這個需求一般都是不懂android的人提出來的)難道