0

I came across this post which talks about how to create a navigation drawer without the use of fragments. Keeping aside the motivation of why I don't want to use fragments, I am now facing an issue such that the onClick event for my RecyclerView items is no longer getting triggered.

TL;DR: If I remove the DrawerLayout section from my layout xml , the onClick event gets triggered as expected. Defining a DrawerLayout in my activity layout xml seems to be causing all events for the RecyclerView such as scrolling/clicking to stop working. Another observation I have is that including the DrawerLaout causes the first element of my RecyclerView to not be shown at all. I am unable to figure out what could be causing the onClick events to get blocked.


Following is the relevant sections of my code:

1. NavigationActivity that will be extended by other activities that want the Navigation Drawer to be shown

public class NavigationActivity extends AppCompatActivity
        implements NavigationView.OnNavigationItemSelectedListener {

    protected void onCreateDrawer() {
        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);
        DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
        ActionBarDrawerToggle toggle = new ActionBarDrawerToggle(
                this, drawer, toolbar, R.string.navigation_drawer_open, R.string.navigation_drawer_close);
        drawer.addDrawerListener(toggle);
        toggle.syncState();

        NavigationView navigationView = (NavigationView) findViewById(R.id.nav_view);
        navigationView.setNavigationItemSelectedListener(this);
    }

    @Override
    public void onBackPressed() {
        DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
        if (drawer.isDrawerOpen(GravityCompat.START)) {
            drawer.closeDrawer(GravityCompat.START);
        } else {
            super.onBackPressed();
        }
    }

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

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        // Handle action bar item clicks here. The action bar will
        // automatically handle clicks on the Home/Up button, so long
        // as you specify a parent activity in AndroidManifest.xml.
        int id = item.getItemId();

        //noinspection SimplifiableIfStatement
        if (id == R.id.action_settings) {
            return true;
        }

        return super.onOptionsItemSelected(item);
    }

    @SuppressWarnings("StatementWithEmptyBody")
    @Override
    public boolean onNavigationItemSelected(MenuItem item) {
        // Handle navigation view item clicks here.
        int id = item.getItemId();

        if (id == R.id.nav_todays_goals) {

        } else if (id == R.id.nav_freetime) {
            Intent intent = new Intent(this,FreeTimeActivity.class);
            startActivity(intent);
        } else if (id == R.id.nav_goaltime) {
            Intent intent = new Intent(this,GoalTimeActivity.class);
            startActivity(intent);
        }

        DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
        drawer.closeDrawer(GravityCompat.START);
        return true;
    }

    @Override
    public void setContentView(@LayoutRes int layoutResID)
    {
        super.setContentView(layoutResID);
        onCreateDrawer();
    }
}

2. One of the activities that extends the NavigationActivity with the problematic RecyclerView (Showing only relevant code) :

public class FreeTimeActivity extends NavigationActivity implements FreeTimeAdapter.FreeTimeAdapterOnClickHandler, LoaderManager.LoaderCallbacks<Cursor> {

    private RecyclerView mRecyclerView;
    private ProgressBar mLoadingIndicator;

    private FreeTimeAdapter mFreeTimeAdapter;

    private int mPosition = RecyclerView.NO_POSITION;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_freetime);

        mRecyclerView = (RecyclerView) findViewById(R.id.recyclerview_seasons);
        mLoadingIndicator = (ProgressBar) findViewById(R.id.pb_loading_indicator);
        mFreeTimeAdapter = new FreeTimeAdapter(this, this);
        LinearLayoutManager layoutManager =
                new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false);

        mRecyclerView.setLayoutManager(layoutManager);
        mRecyclerView.setHasFixedSize(true);
        mRecyclerView.setAdapter(mFreeTimeAdapter);
        showLoading();
        getSupportLoaderManager().initLoader(FREETIME_LOADER_ID, null, this);

   }

    @Override
    public void onClickFreeTimeListItem(String name) {
        Intent intent = new Intent(this, FreeTimeDetailsActivity.class);
        //TODO uri should be set as freetime_goals rather than goals.
        Uri uri = LifeTimeContract.FreeTimeGoalEntry.CONTENT_URI;
        uri = uri.buildUpon().appendPath(name).build();
        intent.setData(uri);
        intent.putExtra("freezone_name",name);
        startActivity(intent);
    }
}

The onClickFreeTimeListItem method is the one that should get called when an item in the mRecyclerView instance is clicked. I tried debugging the FreeTimeAdapter class that is used by the RecyclerView and it seems to register the listener as expected but the onClick method is never called later on when the item in the RecyclerView is clicked. Following is the code for the FreeTimeAdapter class :

public class FreeTimeAdapter extends RecyclerView.Adapter<FreeTimeAdapter.FreeTimeAdapterViewHolder> {

    private final Context mContext;

    final private FreeTimeAdapterOnClickHandler mClickHandler;

    /**
     * The interface that receives onClickFreeTimeListItem messages.
     */
    public interface FreeTimeAdapterOnClickHandler {
        void onClickFreeTimeListItem(String name);
    }

    private Cursor mCursor;

    /**
     * Creates a ForecastAdapter.
     *
     * @param context      Used to talk to the UI and app resources
     * @param clickHandler The on-click handler for this adapter. This single handler is called
     *                     when an item is clicked.
     */
    public FreeTimeAdapter(@NonNull Context context, FreeTimeAdapterOnClickHandler clickHandler) {
        mContext = context;
        mClickHandler = clickHandler;
    }

    @Override
    public FreeTimeAdapterViewHolder onCreateViewHolder(ViewGroup viewGroup, int viewType) {
        int layoutId = R.layout.freetime_list_items;
        View view = LayoutInflater.from(mContext).inflate(layoutId, viewGroup, false);
        view.setFocusable(true);
        return new FreeTimeAdapterViewHolder(view);
    }

    @Override
    public void onBindViewHolder(FreeTimeAdapterViewHolder freeTimeAdapterViewHolder, int position) {
        mCursor.moveToPosition(position);

        String freeTimeName = mCursor.getString(FreeTimeActivity.INDEX_FREETIME_NAME);
        int id = mCursor.getInt(FreeTimeActivity.INDEX_FREETIME_ID);
        String startTime = mCursor.getString(FreeTimeActivity.INDEX_FREETIME_START_TIME);
        String endTime = mCursor.getString(FreeTimeActivity.INDEX_FREETIME_END_TIME);
        String endDate = mCursor.getString(FreeTimeActivity.INDEX_FREETIME_END_DATE);
        freeTimeAdapterViewHolder.tvFreeTimeName.setText(freeTimeName);
        freeTimeAdapterViewHolder.tvStartTime.setText(startTime);
        freeTimeAdapterViewHolder.tvEndTime.setText(endTime);
        freeTimeAdapterViewHolder.tvEndDate.setText(endDate);
        /*FreeTimeItemDataHolder freeTimeItemDataHolder = new FreeTimeItemDataHolder(id,freeTimeName);*/
        freeTimeAdapterViewHolder.itemView.setTag(R.string.tag_freetime_id,id);
        freeTimeAdapterViewHolder.itemView.setTag(R.string.tag_freetime_name,freeTimeName);


    }


    @Override
    public int getItemCount() {
        if (null == mCursor) return 0;
        return mCursor.getCount();
    }

    void swapCursor(Cursor newCursor) {
        mCursor = newCursor;
        notifyDataSetChanged();
    }

    class FreeTimeAdapterViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {

        final TextView tvFreeTimeName;
        final TextView tvStartTime;
        final TextView tvEndTime;
        final TextView tvEndDate;

        FreeTimeAdapterViewHolder(View view) {
            super(view);
            //TODO add additional views to each item in the recyclerview such as delete button etc
            tvFreeTimeName = (TextView) view.findViewById(R.id.tv_freetime_name);
            tvStartTime = (TextView) view.findViewById(R.id.tv_freetime_start_time);
            tvEndTime = (TextView) view.findViewById(R.id.tv_freetime_end_time);
            tvEndDate = (TextView) view.findViewById(R.id.tv_freetime_end_date);
            view.setOnClickListener(this);
        }

        /**
         * This gets called by the child views during a click. We fetch the date that has been
         * selected, and then call the onClickFreeTimeListItem handler registered with this adapter
         *
         * @param v the View that was clicked
         */
        @Override
        public void onClick(View v) {
            int adapterPosition = getAdapterPosition();
            mCursor.moveToPosition(adapterPosition);
            String name = mCursor.getString(FreeTimeActivity.INDEX_FREETIME_NAME);
            mClickHandler.onClickFreeTimeListItem(name);
        }
    }
}

layout xml file for the activity that contains the recycler view

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">


    <ProgressBar
        android:id="@+id/pb_loading_indicator"
        android:layout_width="42dp"
        android:layout_height="42dp"
        android:layout_gravity="center"
        android:visibility="invisible" />

    <android.support.v7.widget.RecyclerView
        android:id="@+id/recyclerview_seasons"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:clipToPadding="false"
        android:paddingBottom="8dp" />

    <android.support.design.widget.FloatingActionButton
        android:id="@+id/fab"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="end|bottom"
        android:src="@drawable/ic_add_box_black_24dp"
        android:layout_margin="48dp"
        android:onClick="onClickAddFreeTime"/>

    <!-- Temporariliy added to navigate to goal time screen. Remove this when app navigation bar is coded -->
    <android.support.design.widget.FloatingActionButton
        android:id="@+id/fab_all_goals"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="start|bottom"
        android:src="@drawable/ic_add_box_black_24dp"
        android:layout_margin="48dp"
        android:onClick="onClickAllGoals"/>

    <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"
        android:fitsSystemWindows="true"
        tools:openDrawer="start">

        <include
            layout="@layout/app_bar_navigation"
            android:layout_width="match_parent"
            android:layout_height="match_parent" />

        <android.support.design.widget.NavigationView
            android:id="@+id/nav_view"
            android:layout_width="wrap_content"
            android:layout_height="match_parent"
            android:layout_gravity="start"
            android:fitsSystemWindows="true"
            app:headerLayout="@layout/nav_header_navigation"
            app:menu="@menu/activity_navigation_drawer" />

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

</FrameLayout>

The same codebase was working fine until I introduced the NavigationActivity and related navigation drawer code.

Ping
  • 587
  • 5
  • 27
  • Is there any visual feedback when clicking? Can you scroll the `RecyclerView`? Are you sure you have the `DrawerLayout` set up correctly in the XML? – Mike M. Aug 12 '18 at 13:10
  • @MikeM. You are right. I am unable to scroll the RecyclerView even though there are 100+ items. What do you suspect could be causing this? I edited the post with more information. Any direction would be really helpful. – Ping Aug 12 '18 at 13:35
  • Yeah, the layout is a little wacky. You don't want to "layer" the `DrawerLayout` over everything else. It should contain two things: all of the main content over which the drawer opens, and the drawer itself. Your main content is split up, and part of it – including the `RecyclerView` – is ending up underneath the `DrawerLayout`. The `` should be the root, and everything else that is not the drawer – the ``, in this case – should be inside one `ViewGroup` listed above it; e.g., arranged inside the `app_bar_navigation` layout somehow – Mike M. Aug 12 '18 at 20:48
  • 1
    @MikeM. Thanks for linking me to the duplicate. Followed your advice of using the `DrawerLayout` as the root and a few changes here and there and the issue was resolved. Thanks. – Ping Aug 15 '18 at 13:42

1 Answers1

0

Hi ping actually i don't know what is the wrong exactly in your code but you can take look on my code i did the same and it's working fine
this is the base activity that contain the nav drawer

public class BaseActivity extends AppCompatActivity implements
        NavigationView.OnNavigationItemSelectedListener {

    private NavigationView navigationView;
    private DrawerLayout fullLayout;
    private Toolbar toolbar;
    private ActionBarDrawerToggle drawerToggle;
    private int selectedNavItemId;
    List<User> userInfoList = new ArrayList<>();
    private TextView headerText;
    private TextView pointsTextView;
    private TextView groupTextView;
    String access;
    String client;
    String expiry;
    String uid;
    String type;
    String url;
    ArrayList<String> categories;
    View parentVeiw;
    @Override
    public void onCreate(Bundle savedInstanceState, PersistableBundle persistentState) {
        super.onCreate(savedInstanceState, persistentState);

    }
    boolean doubleBackToExitPressedOnce = false;

    @Override
    public void onBackPressed() {
        parentVeiw=findViewById(R.id.activity_container);
        if (doubleBackToExitPressedOnce) {
            super.onBackPressed();
            return;
        }

        this.doubleBackToExitPressedOnce = true;
        Snackbar.make(parentVeiw,"الرجاء اضغط مرة أخرى للخروج من البرنامج",Snackbar.LENGTH_SHORT).show();

        new Handler().postDelayed(new Runnable() {

            @Override
            public void run() {
                doubleBackToExitPressedOnce = false;
            }
        }, 2000);
    }

    @Override
    public void setContentView(@LayoutRes int layoutResID) {
        access = ((GlobalVAriables) this.getApplication()).getAccessToken();
        client = ((GlobalVAriables) this.getApplication()).getClient();
        expiry = ((GlobalVAriables) this.getApplication()).getExpiry();
        uid = ((GlobalVAriables) this.getApplication()).getUid();
        type = ((GlobalVAriables) this.getApplication()).getType();
        url = ((GlobalVAriables) this.getApplication()).getUrl();
        getCategories(url, access, type, client, expiry, uid);
        sendJsonRequestgetUserInfo(url, access, type, client, expiry, uid);



        /**
         * This is going to be our actual root layout.
         */
        fullLayout = (DrawerLayout) getLayoutInflater().inflate(R.layout.activity_base, null);
        /**
         * {@link FrameLayout} to inflate the child's view. We could also use a {@link android.view.ViewStub}
         */
        FrameLayout activityContainer = (FrameLayout) fullLayout.findViewById(R.id.activity_content);
        getLayoutInflater().inflate(layoutResID, activityContainer, true);

        /**
         * Note that we don't pass the child's layoutId to the parent,
         * instead we pass it our inflated layout.
         */
        super.setContentView(fullLayout);

        toolbar = (Toolbar) findViewById(R.id.toolbar);
        navigationView = (NavigationView) findViewById(R.id.navigationView); //        int currentapiVersion = android.os.Build.VERSION.SDK_INT; //        if (currentapiVersion == Build.VERSION_CODES.KITKAT){ //            toolbar.setPadding(0, getStatusBarHeight(), 0, 0);  } //

        if (useToolbar())
        {
            setSupportActionBar(toolbar);
        }
        else
        {
            toolbar.setVisibility(View.GONE);
        }

        setUpNavView();
    }

    /**
     * Helper method that can be used by child classes to
     * specify that they don't want a {@link Toolbar}
     * @return true
     */
    protected boolean useToolbar()
    {
        return true;
    }

    protected void setUpNavView()
    {
        navigationView.setNavigationItemSelectedListener(this);

        if( useDrawerToggle()) { // use the hamburger menu
            drawerToggle = new ActionBarDrawerToggle(this, fullLayout, toolbar,
                    R.string.drawer_open,
                    R.string.drawer_close);

            fullLayout.setDrawerListener(drawerToggle);
            drawerToggle.syncState();
        } else if(useToolbar() && getSupportActionBar() != null) {
            // Use home/back button instead
            getSupportActionBar().setDisplayHomeAsUpEnabled(true);
            getSupportActionBar().setHomeAsUpIndicator(getResources()
                    .getDrawable(R.drawable.abc_ic_ab_back_mtrl_am_alpha));
        }
    }

    /**
     * Helper method to allow child classes to opt-out of having the
     * hamburger menu.
     * @return
     */
    protected boolean useDrawerToggle()
    {
        return true;
    }


    @Override
    public boolean onNavigationItemSelected(MenuItem menuItem) {
        fullLayout.closeDrawer(GravityCompat.START);
        selectedNavItemId = menuItem.getItemId();

        return onOptionsItemSelected(menuItem);
    }


    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        // Handle action bar item clicks here. The action bar will
        // automatically handle clicks on the Home/Up button, so long
        // as you specify a parent activity in AndroidManifest.xml.
        int id = item.getItemId();

        //noinspection SimplifiableIfStatement
        switch (id)
        {
            case R.id.main:
                Intent mainac = new Intent(this, MainActivity.class);
                startActivity(mainac);
                finish();


                break;

            case R.id.Categories:
                Intent Categories=new Intent(this,CategoryActivity.class);
                Categories.putExtra("categories", categories);
                startActivity(Categories);
                finish();
                break;
            case R.id.notifications:
                Intent notificationIntent=new Intent(this,NotificationsActivity.class);
                startActivity(notificationIntent);
                finish();

                break;
            case R.id.change_password:
                Intent changePassIntent=new Intent(this,changePassword.class);
                startActivity(changePassIntent);
                finish();

                break;
            case R.id.log_out:



                SharedPreferences sharedPrefs = getSharedPreferences(SignInActivity.PREFS_NAME, MODE_PRIVATE);
                SharedPreferences.Editor editor = sharedPrefs.edit();
                editor.clear();
                editor.commit(); //                user = "";
                //show login form
                Intent intent = new Intent(this, First_Activity.class);
                startActivity(intent);
                finish();

                break;
            case R.id.about_them:
                Intent aboutThemIntent=new Intent(this,AboutThem.class);
                startActivity(aboutThemIntent);
                finish();

                break;
            case R.id.about_us:
                Intent aboutUsIntent=new Intent(this,AboutUs.class);
                startActivity(aboutUsIntent);
                finish();

                break;




            default:
        }

        return super.onOptionsItemSelected(item);
    }}

and this is the activity that extend the base activity

public class MainActivity extends BaseActivity {
    public static final int detail_id = 1;

    private bookListAdapter booklistadapter;

    List<book> aksarEste3araList = new ArrayList<>();

    RecyclerView aksarEste3araRecyclerView;

    List<book> sanfMofadalList = new ArrayList<>();
    RecyclerView sanfMofadalRecyclerView;

    String user;
    String access;
    String client;
    String expiry;
    String uid;
    String type;
    String url;
    TextView mostEste3aeaNmae, sanfMofadalName;
    ProgressDialog progressDialog;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mostEste3aeaNmae = (TextView) findViewById(R.id.most_este3ara_name);
        sanfMofadalName = (TextView) findViewById(R.id.sanf_mofadal_name);
        Typeface custom_font = Typeface.createFromAsset(getAssets(), "fonts/GE_SS_Three_Light_0.otf");

        mostEste3aeaNmae.setTypeface(custom_font);
        sanfMofadalName.setTypeface(custom_font);


        access = ((GlobalVAriables) this.getApplication()).getAccessToken();
        client = ((GlobalVAriables) this.getApplication()).getClient();
        expiry = ((GlobalVAriables) this.getApplication()).getExpiry();
        uid = ((GlobalVAriables) this.getApplication()).getUid();
        type = ((GlobalVAriables) this.getApplication()).getType();
        url = ((GlobalVAriables) this.getApplication()).getUrl();


//Toast.makeText(getBaseContext(),access,Toast.LENGTH_SHORT).show();


        // Get updated InstanceID token.
        String refreshedToken = FirebaseInstanceId.getInstance().getToken();

        // If you want to send messages to this application instance or
        // manage this apps subscriptions on the server side, send the
        // Instance ID token to your app server.
        sendRegistrationToServer(refreshedToken);


        //        aksar este3ara
        aksarEste3araRecyclerView = (RecyclerView) findViewById(R.id.aksar_este3ara_recycler_view);
        sendJsonRequestAksarEste3ara(url, access, type, client, expiry, uid);
        aksarEste3araRecyclerView.addOnItemTouchListener(new RecyclerTouchListener(getBaseContext(), aksarEste3araRecyclerView, new RecyclerTouchListener.ClickListener() {
            @Override
            public void onClick(View view, int position) {
                Intent i = new Intent(getBaseContext(), BookDetailsActivity.class);
                i.putExtra("goo", aksarEste3araList.get(position).getCategoryId());
                i.putExtra("bookId", aksarEste3araList.get(position).getBookId());
                startActivityForResult(i, detail_id);

            }

            @Override
            public void onLongClick(View view, int position) {
                Intent i = new Intent(getBaseContext(), BookDetailsActivity.class);
                i.putExtra("goo", aksarEste3araList.get(position).getCategoryId());
                i.putExtra("bookId", aksarEste3araList.get(position).getBookId());
                startActivityForResult(i, detail_id);

            }
        }));


        //        sanf mofadal
        sanfMofadalRecyclerView = (RecyclerView) findViewById(R.id.sanf_mofadal_recycler_view);

        sendJsonRequestSanfMofadal(url, access, type, client, expiry, uid);

        sanfMofadalRecyclerView.addOnItemTouchListener(new RecyclerTouchListener(getBaseContext(), sanfMofadalRecyclerView, new RecyclerTouchListener.ClickListener() {
            @Override
            public void onClick(View view, int position) {
                Intent i = new Intent(getBaseContext(), BookDetailsActivity.class);
                i.putExtra("goo", sanfMofadalList.get(position).getCategoryId());
                i.putExtra("bookId", sanfMofadalList.get(position).getBookId());
                startActivityForResult(i, detail_id);

            }

            @Override
            public void onLongClick(View view, int position) {
                Intent i = new Intent(getBaseContext(), BookDetailsActivity.class);
                i.putExtra("goo", sanfMofadalList.get(position).getCategoryId());
                i.putExtra("bookId", sanfMofadalList.get(position).getBookId());
                startActivityForResult(i, detail_id);

            }
        }));

    }

please if you need any clarification don't hesitate to ask