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.