Ali Soleimani
Ali Soleimani

Reputation: 300

How does Fragment Navigation Drawer open from right to left?

i am beginner in android and implementing my first app!. i use a Fragment for Navigation Drawer in my app, and i want it to open from right to left. in the fragment tag of the main xml file i set android:layout_gravity="right" but when i run my app and touch the toggle button to open the navigation drawer, suddenly the app crashes! and i receive an error from logcat that is java.lang.IllegalArgumentException: No drawer view found with gravity LEFT . what should i do to solve this problem? thanks so much.

//NavDrawerItem.java

public class NavDrawerItem {
    private boolean showNotify;
    private String title;    

    public NavDrawerItem() {    
    }

    public NavDrawerItem(boolean showNotify, String title) {
        this.showNotify = showNotify;
        this.title = title;
    }

    public boolean isShowNotify() {
        return showNotify;
    }

    public void setShowNotify(boolean showNotify) {
        this.showNotify = showNotify;
    }

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }
}

nav_drawer_row.xml:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:clickable="true">

    <TextView
        android:id="@+id/title"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:paddingLeft="30dp"
        android:paddingTop="10dp"
        android:paddingBottom="10dp"
        android:textSize="15dp"
        android:textStyle="bold" />

</RelativeLayout>

fragment_navigation_drawer.xml:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:layoutDirection="rtl"
    android:background="@android:color/white">    

    <RelativeLayout
        android:id="@+id/nav_header_container"
        android:layout_width="match_parent"
        android:layout_height="140dp"
        android:layout_alignParentTop="true"
        android:background="@color/colorPrimary">

        <ImageView
            android:layout_width="70dp"
            android:layout_height="70dp"
            android:src="@drawable/ic_user"
            android:scaleType="fitCenter"
            android:layout_centerInParent="true" />

    </RelativeLayout>    

    <android.support.v7.widget.RecyclerView        
        android:layout_marginRight="15dp"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_below="@id/nav_header_container"

        android:layout_marginTop="15dp" />

</RelativeLayout>

NavigationDrawerAdapter.java:

public class NavigationDrawerAdapter extends RecyclerView.Adapter<NavigationDrawerAdapter.MyViewHolder> {
    List<NavDrawerItem> data = Collections.emptyList();
    private LayoutInflater inflater;
    private Context context;

    public NavigationDrawerAdapter(Context context, List<NavDrawerItem> data) {
        this.context = context;
        inflater = LayoutInflater.from(context);
        this.data = data;
    }

    public void delete(int position) {
        data.remove(position);
        notifyItemRemoved(position);
    }

    @Override
    public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View view = inflater.inflate(R.layout.nav_drawer_row, parent, false);
        MyViewHolder holder = new MyViewHolder(view);
        return holder;
    }

    @Override
    public void onBindViewHolder(MyViewHolder holder, int position) {
        NavDrawerItem current = data.get(position);
        holder.title.setText(current.getTitle());
    }

    @Override
    public int getItemCount() {
        return data.size();
    }

    class MyViewHolder extends RecyclerView.ViewHolder {
        TextView title;

        public MyViewHolder(View itemView) {
            super(itemView);
            title = (TextView) itemView.findViewById(R.id.title);
        }
    }
}

FragmentDrawer.java:

public class FragmentDrawer extends Fragment {

    private static String TAG = FragmentDrawer.class.getSimpleName();

    private RecyclerView recyclerView;
    private ActionBarDrawerToggle mDrawerToggle;
    private DrawerLayout mDrawerLayout;
    private NavigationDrawerAdapter adapter;
    private View containerView;
    private static String[] titles = null;
    private FragmentDrawerListener drawerListener;

    public FragmentDrawer() {    
    }

    public void setDrawerListener(FragmentDrawerListener listener) {
        this.drawerListener = listener;
    }

    public static List<NavDrawerItem> getData() {
        List<NavDrawerItem> data = new ArrayList<>();    

        // preparing navigation drawer items
        for (int i = 0; i < titles.length; i++) {
            NavDrawerItem navItem = new NavDrawerItem();
            navItem.setTitle(titles[i]);
            data.add(navItem);
        }
        return data;
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        // drawer labels
        titles = getActivity().getResources().getStringArray(R.array.nav_drawer_labels);
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        // Inflating view layout
        View layout = inflater.inflate(R.layout.fragment_navigation_drawer, container, false);
        recyclerView = (RecyclerView) layout.findViewById(R.id.drawerList);

        adapter = new NavigationDrawerAdapter(getActivity(), getData());
        recyclerView.setAdapter(adapter);
        recyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));
        recyclerView.addOnItemTouchListener(new RecyclerTouchListener(getActivity(), recyclerView, new ClickListener() {
            @Override
            public void onClick(View view, int position) {
                drawerListener.onDrawerItemSelected(view, position);
                mDrawerLayout.closeDrawer(containerView);
            }

            @Override
            public void onLongClick(View view, int position) {

            }
        }));

        return layout;
    }


    public void setUp(int fragmentId, DrawerLayout drawerLayout, final Toolbar toolbar) {
        containerView = getActivity().findViewById(fragmentId);
        mDrawerLayout = drawerLayout;
        mDrawerToggle = new ActionBarDrawerToggle(getActivity(), drawerLayout, toolbar, R.string.drawer_open, R.string.drawer_close) {
            @Override
            public void onDrawerOpened(View drawerView) {
                super.onDrawerOpened(drawerView);
                getActivity().invalidateOptionsMenu();
            }

            @Override
            public void onDrawerClosed(View drawerView) {
                super.onDrawerClosed(drawerView);
                getActivity().invalidateOptionsMenu();
            }

            @Override
            public void onDrawerSlide(View drawerView, float slideOffset) {
                super.onDrawerSlide(drawerView, slideOffset);
                toolbar.setAlpha(1 - slideOffset / 2);
            }
        };

        mDrawerLayout.setDrawerListener(mDrawerToggle);
        mDrawerLayout.post(new Runnable() {
            @Override
            public void run() {
                mDrawerToggle.syncState();
            }
        });    
    }

    public static interface ClickListener {
        public void onClick(View view, int position);

        public void onLongClick(View view, int position);
    }

    static class RecyclerTouchListener implements RecyclerView.OnItemTouchListener {

        private GestureDetector gestureDetector;
        private ClickListener clickListener;

        public RecyclerTouchListener(Context context, final RecyclerView recyclerView, final ClickListener clickListener) {
            this.clickListener = clickListener;
            gestureDetector = new GestureDetector(context, new GestureDetector.SimpleOnGestureListener() {
                @Override
                public boolean onSingleTapUp(MotionEvent e) {
                    return true;
                }

                @Override
                public void onLongPress(MotionEvent e) {
                    View child = recyclerView.findChildViewUnder(e.getX(), e.getY());
                    if (child != null && clickListener != null) {
                        clickListener.onLongClick(child, recyclerView.getChildPosition(child));
                    }
                }
            });
        }

        @Override
        public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent e) {

            View child = rv.findChildViewUnder(e.getX(), e.getY());
            if (child != null && clickListener != null && gestureDetector.onTouchEvent(e)) {
                clickListener.onClick(child, rv.getChildPosition(child));
            }
            return false;
        }

        @Override
        public void onTouchEvent(RecyclerView rv, MotionEvent e) {
        }

        @Override
        public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) {    
        }   
    }

    public interface FragmentDrawerListener {
        public void onDrawerItemSelected(View view, int position);
   }
}

activity_main.xml:

<android.support.v4.widget.DrawerLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/drawer_layout"

    android:layout_width="match_parent"
    android:layout_height="match_parent">    

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">

        <LinearLayout
            android:id="@+id/container_toolbar"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="vertical">

            <include
                android:id="@+id/toolbar"
                layout="@layout/toolbar" />
        </LinearLayout>

        <FrameLayout
            android:id="@+id/container_body"
            android:layout_width="fill_parent"
            android:layout_height="0dp"
            android:layout_weight="1" />    

    </LinearLayout>   

    <fragment
        android:id="@+id/fragment_navigation_drawer"
        android:name="com.example.videolabpc.mytoolbar.FragmentDrawer"
        android:layout_width="@dimen/nav_drawer_width"
        android:layout_height="match_parent"
        android:layout_gravity="start"
        app:layout="@layout/fragment_navigation_drawer"
        tools:layout="@layout/fragment_navigation_drawer" />

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

MainActivity.java :

private FragmentDrawer drawerFragment;
drawerFragment = (FragmentDrawer)    getSupportFragmentManager().findFragmentById(R.id.fragment_navigation_drawer);
        drawerFragment.setUp(R.id.fragment_navigation_drawer, (DrawerLayout) findViewById(R.id.drawer_layout), mToolbar);
        drawerFragment.setDrawerListener(this);

the Logcat after crashing :

09-14 04:24:46.193 1539-1539/? E/SensorManager: sensor or listener is null
09-14 04:24:46.194 1539-1539/? E/SensorManager: registerListenerImpl() [Sensor: null, SensorEventListener: com.lge.lockscreen.model.ScreenTurnOffBySensor@5997807] by com.lge.lockscreen.model.ScreenTurnOffBySensor.registerProximitySensor():58
09-14 04:24:46.203 278-2219/? E/lge_audio_pcm_dump: lge_set_dump_config: exit dump_config : 0
09-14 04:24:48.544 278-1070/? E/audio_hw_primary: select_devices: enter and usecase(1)
09-14 04:24:48.545 278-1070/? E/audio_hw_primary: enable_snd_device: enter  2
09-14 04:24:51.913 278-1325/? E/audio_hw_primary: disable_snd_device: enter 2
09-14 04:25:14.826 5149-5149/com.teroject.teroject E/GMPM: GoogleService failed to initialize, status: 10, Missing an expected resource: 'R.string.google_app_id' for initializing Google services.  Possible causes are missing google-services.json or com.google.gms.google-services gradle plugin.
09-14 04:25:14.826 5149-5149/com.teroject.teroject E/GMPM: Scheduler not set. Not logging error/warn.
09-14 04:25:14.881 5149-5181/com.teroject.teroject E/GMPM: Uploading is not possible. App measurement disabled
09-14 04:25:15.600 5149-5149/com.teroject.teroject E/OneSignal: You must initialize OneSignal before modifying tags! Omitting this tag operation.
09-14 04:25:16.658 272-994/? E/BandwidthController: [LG DATA] No such appUid: 10004
09-14 04:25:16.860 272-994/? E/BandwidthController: [LG DATA] No such appUid: 10187
09-14 04:25:16.901 5149-5149/com.teroject.teroject E/MediaProfilesEx-JNI: register_com_lge_media_MediaProfilesEx
09-14 04:25:16.903 5149-5149/com.teroject.teroject E/MediaRecorderEx-JNI: register_com_lge_media_MediaRecorderEx
09-14 04:25:16.905 5149-5149/com.teroject.teroject E/SurfaceControlEx: register_com_lge_view_SurfaceControlEx
09-14 04:25:16.912 5149-5149/com.teroject.teroject E/MediaPlayerEx-jni: register_com_lge_view_MediaPlayerEx
09-14 04:25:21.548 5149-5149/com.teroject.teroject E/AndroidRuntime: FATAL EXCEPTION: main
                                                                     Process: com.teroject.teroject, PID: 5149
                                                                     java.lang.IllegalArgumentException: No drawer view found with gravity LEFT
                                                                         at android.support.v4.widget.DrawerLayout.openDrawer(DrawerLayout.java:1618)
                                                                         at android.support.v7.app.ActionBarDrawerToggle.toggle(ActionBarDrawerToggle.java:290)
                                                                         at android.support.v7.app.ActionBarDrawerToggle.access$100(ActionBarDrawerToggle.java:64)
                                                                         at android.support.v7.app.ActionBarDrawerToggle$1.onClick(ActionBarDrawerToggle.java:200)
                                                                         at android.view.View.performClick(View.java:4764)
                                                                         at android.view.View$PerformClick.run(View.java:19844)
                                                                         at android.os.Handler.handleCallback(Handler.java:739)
                                                                         at android.os.Handler.dispatchMessage(Handler.java:95)
                                                                         at android.os.Looper.loop(Looper.java:135)
                                                                         at android.app.ActivityThread.main(ActivityThread.java:5354)
                                                                         at java.lang.reflect.Method.invoke(Native Method)
                                                                         at java.lang.reflect.Method.invoke(Method.java:372)
                                                                         at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:908)
                                                                         at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:703)
09-14 04:25:21.579 278-1070/? E/audio_hw_primary: select_devices: enter and usecase(1)
09-14 04:25:21.579 278-1070/? E/audio_hw_primary: enable_snd_device: enter  2
09-14 04:25:21.766 1001-1483/? E/ConnectivityService: RemoteException caught trying to send a callback msg for NetworkRequest [ id=8, legacyType=-1, [ Capabilities: INTERNET&NOT_RESTRICTED&TRUSTED&NOT_VPN] ]
09-14 04:25:22.510 2648-2648/? E/NetworkScheduler.SR: Invalid parameter app
09-14 04:25:22.510 2648-2648/? E/NetworkScheduler.SR: Invalid package name : Perhaps you didn't include a PendingIntent in the extras?
09-14 04:25:22.562 3927-4157/? E/Drive.UninstallOperation: Package still installed com.teroject.teroject
09-14 04:25:22.730 5547-5547/? E/Finsky: [1] com.google.android.finsky.wear.bo.a(838): onConnectionFailed: ConnectionResult{statusCode=API_UNAVAILABLE, resolution=null, message=null}
09-14 04:25:23.217 3927-5659/? E/IntentOperationSvc: Failed to instantiate Chimera operation impl, dropping operation
09-14 04:25:23.223 5547-5547/? E/Finsky: [1] com.google.android.finsky.wear.bo.a(838): onConnectionFailed: ConnectionResult{statusCode=API_UNAVAILABLE, resolution=null, message=null}
09-14 04:25:24.332 272-994/? E/BandwidthController: [LG DATA] No such appUid: 10187
09-14 04:25:24.585 2648-2648/? E/NetworkScheduler.SR: Invalid parameter app
09-14 04:25:24.585 2648-2648/? E/NetworkScheduler.SR: Invalid package name : Perhaps you didn't include a PendingIntent in the extras?
09-14 04:25:24.776 278-1325/? E/audio_hw_primary: disable_snd_device: enter 2
09-14 04:25:24.799 5860-5860/? E/App: [App][onCreate()] 4.40.21
09-14 04:25:26.507 272-994/? E/BandwidthController: [LG DATA] No such appUid: 10101
09-14 04:25:27.341 272-994/? E/BandwidthController: [LG DATA] No such appUid: 10100
09-14 04:25:29.554 5901-5936/? E/a: Failed to send crash attachment report /data/data/com.instagram.android/app_minidumps/307ff5ad80207d193ff0aa19e8633a8a.dmp
                                    com.facebook.g.b.c: Error while sending report to Http Post Form.
                                        at com.facebook.g.b.d.a(HttpPostSender.java:110)
                                        at com.facebook.g.n.b(ErrorReporter.java:1436)
                                        at com.facebook.g.n.a(ErrorReporter.java:1626)
                                        at com.facebook.g.n.a(ErrorReporter.java:1483)
                                        at com.facebook.g.n.a(ErrorReporter.java:118)
                                        at com.facebook.g.i.a(ErrorReporter.java:348)
                                        at com.facebook.g.i.run(ErrorReporter.java:360)
                                     Caused by: java.net.SocketTimeoutException: failed to connect to www.facebook.com/10.10.34.36 (port 443) after 3000ms
                                        at libcore.io.IoBridge.connectErrno(IoBridge.java:169)
                                        at libcore.io.IoBridge.connect(IoBridge.java:122)
                                        at java.net.PlainSocketImpl.connect(PlainSocketImpl.java:183)
                                        at java.net.PlainSocketImpl.connect(PlainSocketImpl.java:456)
                                        at java.net.Socket.connect(Socket.java:886)
                                        at com.android.okhttp.internal.Platform.connectSocket(Platform.java:139)
                                        at com.android.okhttp.Connection.connect(Connection.java:155)
                                        at com.android.okhttp.Connection.connect(Connection.java:170)
                                        at com.android.okhttp.internal.http.HttpEngine.connect(HttpEngine.java:309)
                                        at com.android.okhttp.internal.http.HttpEngine.sendRequest(HttpEngine.java:242)
                                        at com.android.okhttp.internal.http.HttpURLConnectionImpl.execute(HttpURLConnectionImpl.java:397)
                                        at com.android.okhttp.internal.http.HttpURLConnectionImpl.connect(HttpURLConnectionImpl.java:118)
                                        at com.android.okhttp.internal.http.HttpURLConnectionImpl.getOutputStream(HttpURLConnectionImpl.java:229)
                                        at com.android.okhttp.internal.http.DelegatingHttpsURLConnection.getOutputStream(DelegatingHttpsURLConnection.java:218)
                                        at com.android.okhttp.internal.http.HttpsURLConnectionImpl.getOutputStream(HttpsURLConnectionImpl.java:25)
                                        at com.facebook.g.c.f.a(HttpRequest.java:52)
                                        at com.facebook.g.b.d.b(HttpPostSender.java:134)
                                        at com.facebook.g.b.d.a(HttpPostSender.java:108)
                                        at com.facebook.g.n.b(ErrorReporter.java:1436) 
                                        at com.facebook.g.n.a(ErrorReporter.java:1626) 
                                        at com.facebook.g.n.a(ErrorReporter.java:1483) 
                                        at com.facebook.g.n.a(ErrorReporter.java:118) 
                                        at com.facebook.g.i.a(ErrorReporter.java:348) 
                                        at com.facebook.g.i.run(ErrorReporter.java:360) 
09-14 04:25:32.298 5901-5901/? E/ActivityThread: Failed to find provider info for com.facebook.katana.provider.AttributionIdProvider
09-14 04:25:32.311 5901-5954/? E/ActivityThread: Failed to find provider info for com.facebook.katana.provider.AttributionIdProvider
09-14 04:25:32.446 272-994/? E/BandwidthController: [LG DATA] No such appUid: 10101
09-14 04:25:32.727 272-994/? E/BandwidthController: [LG DATA] No such appUid: 10101

Upvotes: 0

Views: 829

Answers (2)

Alon Kogan
Alon Kogan

Reputation: 3418

For me adding the following property:

android:layoutDirection="rtl"

to the DrawerLayout and NavigationView Solved the problem

Upvotes: 1

Karakuri
Karakuri

Reputation: 38595

The problem is ActionBarDrawerToggle always expects the drawer to be on the side corresponding to Gravity.START. In most locales this is on the left side, hence it tries to open the drawer with Gravity.LEFT, but yours is on the other side so it throws an exception instead. Whether or not this seems like an intelligent design decision, it is what it is.

If you are following the Material Design guidelines, your drawer will cover the action bar when it opens (it might already be doing this). Specifically for this reason, I don't like using ActionBarDrawerToggle and don't find it particularly useful; the animation of the menu icon to an arrow is obscured by the drawer, and the other functionality is easy enough to implement directly in the activity. In your case, the animation might actually be useful if your drawer on the other side doesn't cover the icon, but then again you might not want the icon to animate at all anyway.

In your onCreate() method, all you need to do is

  1. Call toolbar.setNavigationOnClickListener() (or set an OnClickListener on a view in your Toolbar) and have this listener toggle the drawer using drawerLayout.openDrawer(Gravity.RIGHT) and the equivalent closeDrawer() method.
  2. Use drawerLayout.addDrawerListener() to add a listener. When the drawer opens or closes, you can use this listener to change things like the toolbar's icon and/or its contentDescription (for accessibility), or to do anything else you like

Upvotes: 1

Related Questions