2013-07-05 40 views
9

Tôi đang cố gắng sử dụng SlidingPaneLayout với ViewPager, như vậySử dụng SlidingPaneLayout của Android với ViewPager

<?xml version="1.0" encoding="utf-8"?> 

<android.support.v4.widget.SlidingPaneLayout 
     xmlns:android="http://schemas.android.com/apk/res/android" 
     android:id="@+id/scientific_graph_slidingPaneLayout" 
     android:layout_width="match_parent" 
     android:layout_height="match_parent"> 

    <!-- 
     The first child view becomes the left pane. 
    --> 

    <ListView 
      android:id="@+id/left_pane" 
      android:layout_width="240dp" 
      android:layout_height="match_parent" 
      android:layout_gravity="left" /> 
    <!-- 
     The second child becomes the right (content) pane. 
    --> 

    <android.support.v4.view.ViewPager 
      android:id="@+id/scientific_graph_viewPager" 
      android:layout_width="match_parent" 
      android:layout_height="match_parent"> 
    </android.support.v4.view.ViewPager> 

</android.support.v4.widget.SlidingPaneLayout> 

Các SlidingPaneLayout trượt khi tôi kéo từ cạnh bên trái; tuy nhiên, tôi không thể làm cho ViewPager trượt xuống khi tôi kéo từ cạnh phải. Khi tôi kéo từ cạnh phải, nó trượt rất ít và sau đó chụp lại.

Làm điều này thậm chí có thể? Có cách nào tốt hơn để làm điều này?

Tôi thấy rằng bằng cách di chuyển ngón tay của tôi lên và sang trái, tôi có thể vuốt trang xem.

+0

Tôi đã cố gắng làm điều tương tự. Có ai biết làm thế nào để làm cho slidepane mở chỉ khi kéo từ cạnh? để nếu không kéo ra khỏi cạnh, viewpager sẽ trượt. – Zul

Trả lời

17

Nguyên nhân gốc là việc triển khai #onInterceptTouchEvent. Việc triển khai SlidingPaneLayout cũ hơn đã thực hiện lệnh gọi #canScroll, thao tác này sẽ kiểm tra xem mục tiêu cảm ứng có thể cuộn hay không và nếu có, sẽ cuộn mục tiêu cảm ứng thay vì trượt bảng điều khiển. Việc thực hiện gần đây nhất có vẻ như luôn chặn đứng sự kiện chuyển động, khi ngưỡng kéo vượt quá ngưỡng, trừ trường hợp X kéo vượt quá độ dốc và kéo Y vượt quá mức kéo X (như được ghi bởi OP).

Một giải pháp cho việc này là sao chép SlidingPaneLayout và thực hiện một vài thay đổi để làm cho tính năng này hoạt động. Những thay đổi này là:

  1. Sửa đổi các trường hợp ACTION_MOVE trong #onInterceptTouchEvent cũng để kiểm tra #canScroll,

    if (adx > slop && ady > adx || 
        canScroll(this, false, Math.round(x - mInitialMotionX), Math.round(x), Math.round(y))) 
    { ... } 
    
  2. Sửa đổi các kiểm tra cuối cùng trong #canScroll đến trường hợp đặc biệt ViewPager. Sửa đổi này cũng có thể được thực hiện trong một lớp con bằng cách ghi đè #canScroll, vì nó không truy cập bất kỳ trạng thái riêng tư nào.

    protected boolean canScroll(View v, boolean checkV, int dx, int x, int y) { 
        ... 
        /* special case ViewPagers, which don't properly implement the scrolling interface */ 
        return checkV && (ViewCompat.canScrollHorizontally(v, -dx) || 
         ((v instanceof ViewPager) && canViewPagerScrollHorizontally((ViewPager) v, -dx))) 
    } 
    
    boolean canViewPagerScrollHorizontally(ViewPager p, int dx) { 
        return !(dx < 0 && p.getCurrentItem() <= 0 || 
         0 < dx && p.getAdapter().getCount() - 1 <= p.getCurrentItem());  
    } 
    

khả năng Có một cách thanh lịch hơn để làm điều này bằng cách sửa chữa các ViewDragHelper, nhưng đây là một cái gì đó Google sẽ giải quyết trong một bản cập nhật tương lai của gói hỗ trợ. Các hacks ở trên sẽ có được bố trí làm việc với ViewPagers (và các thùng chứa cuộn theo chiều ngang?) Bây giờ.

+0

Làm việc ra khỏi hộp! Cảm ơn bạn! –

+0

Câu trả lời rất hay! Cảm ơn bạn! – hooloovoo

7

Xây dựng giải pháp @Brien Colwell, tôi đã viết một lớp con tùy chỉnh của SlidingPaneLayout xử lý việc này cho bạn và cũng thêm cạnh vuốt để khi người dùng cuộn sang bên phải, họ không phải cuộn tất cả các con đường trở lại bên trái để mở cửa sổ.

Vì đây là một phân lớp của SlidingPaneLayout, bạn không cần phải thay đổi bất kỳ tham chiếu nào trong Java, chỉ cần đảm bảo rằng bạn khởi tạo một thể hiện của lớp này (thường là trong XML của bạn).

package com.ryanharter.android.view; 

import android.content.Context; 
import android.support.v4.view.MotionEventCompat; 
import android.support.v4.widget.SlidingPaneLayout; 
import android.util.AttributeSet; 
import android.view.MotionEvent; 
import android.view.ViewConfiguration; 

/** 
* SlidingPaneLayout that, if closed, checks if children can scroll before it intercepts 
* touch events. This allows it to contain horizontally scrollable children without 
* intercepting all of their touches. 
* 
* To handle cases where the user is scrolled very far to the right, but should still be 
* able to open the pane without the need to scroll all the way back to the start, this 
* view also adds edge touch detection, so it will intercept edge swipes to open the pane. 
*/ 
public class PagerEnabledSlidingPaneLayout extends SlidingPaneLayout { 

    private float mInitialMotionX; 
    private float mInitialMotionY; 
    private float mEdgeSlop; 

    public PagerEnabledSlidingPaneLayout(Context context) { 
     this(context, null); 
    } 

    public PagerEnabledSlidingPaneLayout(Context context, AttributeSet attrs) { 
     this(context, attrs, 0); 
    } 

    public PagerEnabledSlidingPaneLayout(Context context, AttributeSet attrs, int defStyle) { 
     super(context, attrs, defStyle); 

     ViewConfiguration config = ViewConfiguration.get(context); 
     mEdgeSlop = config.getScaledEdgeSlop(); 
    } 

    @Override 
    public boolean onInterceptTouchEvent(MotionEvent ev) { 

     switch (MotionEventCompat.getActionMasked(ev)) { 
      case MotionEvent.ACTION_DOWN: { 
       mInitialMotionX = ev.getX(); 
       mInitialMotionY = ev.getY(); 
       break; 
      } 

      case MotionEvent.ACTION_MOVE: { 
       final float x = ev.getX(); 
       final float y = ev.getY(); 
       // The user should always be able to "close" the pane, so we only check 
       // for child scrollability if the pane is currently closed. 
       if (mInitialMotionX > mEdgeSlop && !isOpen() && canScroll(this, false, 
         Math.round(x - mInitialMotionX), Math.round(x), Math.round(y))) { 

        // How do we set super.mIsUnableToDrag = true? 

        // send the parent a cancel event 
        MotionEvent cancelEvent = MotionEvent.obtain(ev); 
        cancelEvent.setAction(MotionEvent.ACTION_CANCEL); 
        return super.onInterceptTouchEvent(cancelEvent); 
       } 
      } 
     } 

     return super.onInterceptTouchEvent(ev); 
    } 
} 
+0

Mã này thật tuyệt vời! – Gilian

Các vấn đề liên quan