滑动手势不支持用手指在 android 的滑动抽屉中的列表视图上

Posted

技术标签:

【中文标题】滑动手势不支持用手指在 android 的滑动抽屉中的列表视图上【英文标题】:Swipe gesture not supporting with finger over listview in sliding drawer in android 【发布时间】:2014-11-20 03:38:04 【问题描述】:

现在我正在使用带有列表视图的滑动抽屉控件。 我的问题是,我无法用手指在列表视图上滑动,但菜单(Toggle)按钮工作正常。 有没有人遇到过这样的情况,如果有,请帮我解决这个问题。 请提出建议。

我希望下面的图片能让您清楚地了解我的问题

这是我的参考工作来源

activity_main.xml

<com.entropy.slidingmenu2.layout.MainLayout     xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_
android:layout_ >

 <!-- This holds our menu -->
 <LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_
    android:layout_
    android:orientation="vertical" >

    <ListView
        android:id="@+id/activity_main_menu_listvie"
        android:layout_
        android:layout_
        android:background="#B4D609"
        android:cacheColorHint="#00000000" >
    </ListView>
</LinearLayout>

<!-- This holds our content-->
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_
    android:layout_
    android:orientation="vertical" >

    <!-- This acts as Actionbar -->
    <LinearLayout
        android:layout_
        android:layout_
        android:background="#96D6E3"
        android:orientation="horizontal" >

        <Button
            android:layout_
            android:layout_
            android:onClick="toggleMenu"
            android:text="Menu"
            android:id="@+id/activity_main_content_button_menu" />

        <TextView
             android:layout_
             android:layout_
             android:text="@string/app_name"
             android:gravity="center"
             android:id="@+id/activity_main_content_title"
             android:layout_weight="1" />

    </LinearLayout>       

    <!-- This is where fragment will show up -->
    <FrameLayout
        android:id="@+id/activity_main_content_fragment"
        android:layout_
        android:layout_ >
        </FrameLayout>
     </LinearLayout>
</com.entropy.slidingmenu2.layout.MainLayout>

fragment_listview.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_
android:layout_
android:background="@android:color/white" >

<ListView
    android:layout_
    android:layout_
    android:id="@+id/fragment_listview_listview" />
</RelativeLayout>

MainActiviy.java

public class MainActivity extends FragmentActivity 

MainLayout mainLayout;
private ListView lvMenu;
private String[] lvMenuItems;
Button btMenu;
TextView tvTitle;

@Override
protected void onCreate(Bundle savedInstanceState) 
    super.onCreate(savedInstanceState);

    mainLayout = (MainLayout)this.getLayoutInflater().inflate(R.layout.activity_main, null);
    setContentView(mainLayout);

    lvMenuItems = getResources().getStringArray(R.array.menu_items);
    lvMenu = (ListView) findViewById(R.id.activity_main_menu_listvie);
    lvMenu.setAdapter(new ArrayAdapter<String>(this,
            android.R.layout.simple_list_item_1, lvMenuItems));
    lvMenu.setOnItemClickListener(new OnItemClickListener() 
        @Override
        public void onItemClick(AdapterView<?> parent, View view, int position, long id) 
            onMenuItemClick(parent, view, position, id);
        
    );
    btMenu = (Button) findViewById(R.id.activity_main_content_button_menu);
    btMenu.setOnClickListener(new OnClickListener() 
        @Override
        public void onClick(View v) 
            // Show/hide the menu
            toggleMenu(v);
        
    );
    tvTitle = (TextView) findViewById(R.id.activity_main_content_title);
    // Add FragmentMain as the initial fragment       
    FragmentManager fm = MainActivity.this.getSupportFragmentManager();
    FragmentTransaction ft = fm.beginTransaction();

    FragmentMain fragment = new FragmentMain();
    ft.add(R.id.activity_main_content_fragment, fragment);
    ft.commit();   


@Override
public boolean onCreateOptionsMenu(Menu menu) 
    // Inflate the menu; this adds items to the action bar if it is present.
    getMenuInflater().inflate(R.menu.main, menu);
    return true;


public void toggleMenu(View v)
    mainLayout.toggleMenu();


private void onMenuItemClick(AdapterView<?> parent, View view, int position, long id) 
    String selectedItem = lvMenuItems[position];
    String currentItem = tvTitle.getText().toString();

    // Do nothing if selectedItem is currentItem
    if(selectedItem.compareTo(currentItem) == 0) 
        mainLayout.toggleMenu();
        return;
    
    FragmentManager fm = MainActivity.this.getSupportFragmentManager();
    FragmentTransaction ft = fm.beginTransaction();
    Fragment fragment = null;


    if(selectedItem.compareTo("ListView") == 0) 
        fragment = new FragmentListView();
    

    if(fragment != null) 
        // Replace current fragment by this new one
        ft.replace(R.id.activity_main_content_fragment, fragment);
        ft.commit();

        // Set title accordingly
        tvTitle.setText(selectedItem);
    
    mainLayout.toggleMenu();


@Override
public void onBackPressed() 
    if (mainLayout.isMenuShown()) 
        mainLayout.toggleMenu();
    
    else 
        super.onBackPressed();
    

FragmentListView.java

public class FragmentListView extends Fragment 
ListView listView;

public FragmentListView() 


@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
        Bundle savedInstanceState) 
    View view = inflater.inflate(R.layout.fragment_listview, null);

    String[] listViewItems = new String[] "Sachin Tendulkar", "Sourav Ganguly", "Rahul Dravid", "Virendar Shewag", 
            "Yuvraj Singh","Mahendra Singh Dhoni","Irfan Pathan","Bhuvaneshawar kumar","Harbhajan Singh",
            "Zaheer Khan","Ashish Nehra","Virat Kohli";
    listView = (ListView) view.findViewById(R.id.fragment_listview_listview);
    listView.setAdapter(new ArrayAdapter<String>(getActivity(),
            android.R.layout.simple_list_item_1, listViewItems));

    return view;

MainLayout.java

public class MainLayout extends LinearLayout 

// Duration of sliding animation, in miliseconds
private static final int SLIDING_DURATION = 500;

// Query Scroller every 16 miliseconds
private static final int QUERY_INTERVAL = 16;

// MainLayout width
int mainLayoutWidth;

// Sliding menu
private View menu;

// Main content
private View content;

// menu does not occupy some right space
// This should be updated correctly later in onMeasure
private static int menuRightMargin = 150;

// The state of menu
private enum MenuState 
    HIDING,
    HIDDEN,
    SHOWING,
    SHOWN,
;

// content will be layouted based on this X offset
// Normally, contentXOffset = menu.getLayoutParams().width = this.getWidth - menuRightMargin
private int contentXOffset;

// menu is hidden initially
private MenuState currentMenuState = MenuState.HIDDEN;

// Scroller is used to facilitate animation
private Scroller menuScroller = new Scroller(this.getContext(),
        new EaseInInterpolator());

// Used to query Scroller about scrolling position
// Note: The 3rd paramter to startScroll is the distance
private Runnable menuRunnable = new MenuRunnable();
private Handler menuHandler = new Handler();

// Previous touch position
int prevX = 0;

// Is user dragging the content
boolean isDragging = false;

// Used to facilitate ACTION_UP 
int lastDiffX = 0;

// Constructor

// 3 parameters constructor seems to be unavailable in 2.3
/*
public MainLayout(Context context, AttributeSet attrs, int defStyle) 
    super(context, attrs, defStyle);

*/

public MainLayout(Context context, AttributeSet attrs) 
    super(context, attrs);


public MainLayout(Context context) 
    super(context);


// Overriding LinearLayout core methods

// Ask all children to measure themselves and compute the measurement of this
// layout based on the children
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) 
    super.onMeasure(widthMeasureSpec, heightMeasureSpec);

    mainLayoutWidth = MeasureSpec.getSize(widthMeasureSpec);
    menuRightMargin = mainLayoutWidth * 10 / 100;
    // Nothing to do, since we only care about how to layout


// This is called when MainLayout is attached to window
// At this point it has a Surface and will start drawing. 
// Note that this function is guaranteed to be called before onDraw
@Override
protected void onAttachedToWindow() 
    super.onAttachedToWindow();

    // Get our 2 child View
    menu = this.getChildAt(0);
    content = this.getChildAt(1);   

    // Attach View.OnTouchListener
    content.setOnTouchListener(new OnTouchListener() 
        @Override
        public boolean onTouch(View v, MotionEvent event) 
            return MainLayout.this.onContentTouch(v, event);
        
    );

    // Initially hide the menu
    menu.setVisibility(View.GONE);


// Called from layout when this view should assign a size and position to each of its children
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) 
    //Log.d("MainLayout.java onLayout()", "left " + left + " top " + top + " right " + right + " bottom " + bottom);
    //Log.d("MainLayout.java onLayout()", "getHeight " + this.getHeight() + " getWidth " + this.getWidth());

    // True if MainLayout 's size and position has changed
    // If true, calculate child views size
    if(changed) 
        // Note: LayoutParams are used by views to tell their parents how they want to be laid out

        //Log.d("MainLayout.java onLayout()", "changed " + changed);

        // content View occupies the full height and width
        LayoutParams contentLayoutParams = (LayoutParams)content.getLayoutParams();
        contentLayoutParams.height = this.getHeight();
        contentLayoutParams.width = this.getWidth();

        // menu View occupies the full height, but certain width
        LayoutParams menuLayoutParams = (LayoutParams)menu.getLayoutParams();
        menuLayoutParams.height = this.getHeight();
        menuLayoutParams.width = this.getWidth() - menuRightMargin;          
    

    // Layout the child views    
    menu.layout(left, top, right - menuRightMargin, bottom);
    content.layout(left + contentXOffset, top, right + contentXOffset, bottom);



// Custom methods for MainLayout

// Used to show/hide menu accordingly
public void toggleMenu() 
    // Do nothing if sliding is in progress
    if(currentMenuState == MenuState.HIDING || currentMenuState == MenuState.SHOWING)
        return;

    switch(currentMenuState) 
    case HIDDEN:
        currentMenuState = MenuState.SHOWING;
        menu.setVisibility(View.VISIBLE);
        menuScroller.startScroll(0, 0, menu.getLayoutParams().width,
                0, SLIDING_DURATION);
        break;
    case SHOWN:
        currentMenuState = MenuState.HIDING;
        menuScroller.startScroll(contentXOffset, 0, -contentXOffset, 
                0, SLIDING_DURATION);
        break;
    default:
        break;
    

    // Begin querying
    menuHandler.postDelayed(menuRunnable, QUERY_INTERVAL);

    // Invalite this whole MainLayout, causing onLayout() to be called
    this.invalidate();


// Query Scroller
protected class MenuRunnable implements Runnable 
    @Override
    public void run() 
        boolean isScrolling = menuScroller.computeScrollOffset();
        adjustContentPosition(isScrolling);
    


// Adjust content View position to match sliding animation
private void adjustContentPosition(boolean isScrolling) 
    int scrollerXOffset = menuScroller.getCurrX();

    //Log.d("MainLayout.java adjustContentPosition()", "scrollerOffset " + scrollerOffset);

    // Translate content View accordingly
    content.offsetLeftAndRight(scrollerXOffset - contentXOffset);

    contentXOffset = scrollerXOffset;

    // Invalite this whole MainLayout, causing onLayout() to be called
    this.invalidate();

    // Check if animation is in progress
    if (isScrolling)
        menuHandler.postDelayed(menuRunnable, QUERY_INTERVAL);
    else
        this.onMenuSlidingComplete();


// Called when sliding is complete
private void onMenuSlidingComplete() 
    switch (currentMenuState) 
    case SHOWING:
        currentMenuState = MenuState.SHOWN;
        break;
    case HIDING:
        currentMenuState = MenuState.HIDDEN;
        menu.setVisibility(View.GONE);
        break;
    default:
        return;
    


// Make scrolling more natural. Move more quickly at the end
// See the formula here http://cyrilmottier.com/2012/05/22/the-making-of-prixing-fly-in-app-menu-part-1/
protected class EaseInInterpolator implements Interpolator 
    @Override
    public float getInterpolation(float t) 
        return (float)Math.pow(t-1, 5) + 1;
    



// Is menu completely shown
public boolean isMenuShown() 
    return currentMenuState == MenuState.SHOWN;


// Handle touch event on content View
public boolean onContentTouch(View v, MotionEvent event) 
    // Do nothing if sliding is in progress
    if(currentMenuState == MenuState.HIDING || currentMenuState == MenuState.SHOWING)
        return false;

    // getRawX returns X touch point corresponding to screen
    // getX sometimes returns screen X, sometimes returns content View X
    int curX = (int)event.getRawX();
    int diffX = 0;

    switch(event.getAction()) 
    case MotionEvent.ACTION_DOWN:
        //Log.d("MainLayout.java onContentTouch()", "Down x " + curX);

        prevX = curX;
        return true;

    case MotionEvent.ACTION_MOVE:
        //Log.d("MainLayout.java onContentTouch()", "Move x " + curX);

        // Set menu to Visible when user start dragging the content View
        if(!isDragging) 
            isDragging = true;
            menu.setVisibility(View.VISIBLE);
        

        // How far we have moved since the last position
        diffX = curX - prevX;

        // Prevent user from dragging beyond border
        if(contentXOffset + diffX <= 0) 
            // Don't allow dragging beyond left border
            // Use diffX will make content cross the border, so only translate by -contentXOffset
            diffX = -contentXOffset;
         else if(contentXOffset + diffX > mainLayoutWidth - menuRightMargin) 
            // Don't allow dragging beyond menu width
            diffX = mainLayoutWidth - menuRightMargin - contentXOffset;
        

        // Translate content View accordingly
        content.offsetLeftAndRight(diffX);

        contentXOffset += diffX;

        // Invalite this whole MainLayout, causing onLayout() to be called
        this.invalidate();

        prevX = curX;
        lastDiffX = diffX;
        return true;

    case MotionEvent.ACTION_UP:
        //Log.d("MainLayout.java onContentTouch()", "Up x " + curX);

        Log.d("MainLayout.java onContentTouch()", "Up lastDiffX " + lastDiffX);

        // Start scrolling
        // Remember that when content has a chance to cross left border, lastDiffX is set to 0
        if(lastDiffX > 0) 
            // User wants to show menu
            currentMenuState = MenuState.SHOWING;

            // No need to set to Visible, because we have set to Visible in ACTION_MOVE
            //menu.setVisibility(View.VISIBLE);

            //Log.d("MainLayout.java onContentTouch()", "Up contentXOffset " + contentXOffset);

            // Start scrolling from contentXOffset
            menuScroller.startScroll(contentXOffset, 0, menu.getLayoutParams().width - contentXOffset,
                    0, SLIDING_DURATION);
         else if(lastDiffX < 0) 
            // User wants to hide menu
            currentMenuState = MenuState.HIDING;
            menuScroller.startScroll(contentXOffset, 0, -contentXOffset, 
                    0, SLIDING_DURATION);
        

        // Begin querying
        menuHandler.postDelayed(menuRunnable, QUERY_INTERVAL);

        // Invalite this whole MainLayout, causing onLayout() to be called
        this.invalidate();

        // Done dragging
        isDragging = false;
        prevX = 0;
        lastDiffX = 0;
        return true;

    default:
        break;
    

    return false;

【问题讨论】:

【参考方案1】:

这不是来自官方 Android SDK 的滑动抽屉。这是一个定制的。正如文档中所述,您需要阅读资源以添加滑动。为此,我建议您阅读this 文章。

【讨论】:

以上是关于滑动手势不支持用手指在 android 的滑动抽屉中的列表视图上的主要内容,如果未能解决你的问题,请参考以下文章

android 手势操作中滑动和滚动的区别

Android中的手势滑动

在Xamarin的View上手指滑动发布活动

导航抽屉 - 禁用滑动

当用户滑动并点击手指(使用触摸手势)时如何隐藏所有 CollectionViewCell?

android下滑动返回上一个页面如何实现