How to apply an onClick Listener to an imageView within a CardView - android-recyclerview

I'm trying to setup an onClick Listener where that listener already has a button. The purpose is to provide a second way to access an intent (in this case a "ReadActivity" class. An example of this is
Here is my code:
#Override
public void onBindViewHolder(MyViewHolder holder, final int position) {
holder.tv_book_title.setText(mData.get(position).getBookTitle());
holder.tv_book_author.setText("by " + mData.get(position).getBookAuthor());
holder.img_book_thumbnail.setImageResource(mData.get(position).getBookId());
holder.actionButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Intent intent = new Intent(mContext, ReadActivity.class);
// passing data to the book activity
intent.putExtra("Id", mData.get(position).getPhotoId());
intent.putExtra("Title", mData.get(position).getBookTitle());
intent.putExtra("Author", mData.get(position).getBookAuthor());
intent.putExtra("Description", mData.get(position).getContentUrl());
intent.putExtra("Thumbnail", mData.get(position).getBookId());
// start the activity
mContext.startActivity(intent);
}
});
And this is my cardview_item xml:
<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.CardView 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/card_view"
android:layout_width="match_parent"
android:layout_height="#dimen/card_height"
android:layout_gravity="center"
android:layout_marginBottom="#dimen/md_keylines"
android:layout_marginLeft="#dimen/md_keylines"
android:layout_marginRight="#dimen/md_keylines"
android:foreground="?attr/selectableItemBackground">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<ImageView
android:id="#+id/book_img_id"
**android:onClick="imageClick"**
android:layout_width="match_parent"
android:layout_height="#dimen/card_image_height"
android:scaleType="centerCrop"
android:contentDescription="#string/todo" />
<TextView
android:id="#+id/book_title_id"
android:layout_width="match_parent"
android:layout_height="#dimen/card_title_height"
android:layout_alignBottom="#+id/book_img_id"
android:layout_marginStart="#dimen/md_keylines"
android:textAppearance="#style/TextAppearance.AppCompat.Title"
android:textColor="#color/white" />
<TextView
android:id="#+id/book_author_id"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="#+id/book_img_id"
android:layout_marginLeft="#dimen/md_keylines"
android:layout_marginTop="#dimen/md_keylines"
android:layout_marginBottom="#dimen/md_keylines"
android:layout_marginRight="#dimen/md_keylines"
android:ellipsize="end"
android:singleLine="true"
android:textColor="#color/dark_grey"
android:textSize="#dimen/article_subheading" />
<Button
android:id="#+id/action_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="#+id/book_author_id"
style="?android:attr/borderlessButtonStyle"
android:textColor="?attr/colorPrimary"
android:text="#string/read" />
</RelativeLayout>
</android.support.v7.widget.CardView>
In the xml, I tried to set a onClick line, but I'm not sure how to setup the onClick listener. Is there a way to do this without affecting the originial code? Or would it be easier to copy to actionButton.setOnClickListener and just change the holder setting to the thumbnail?

You should not use `android:onClick" attribute for handling click. You better handle it in code so that your Layout and logic not tightly coupled.
You need to use setOnClickListener like at the actionButton. Or you can slightly change your code to handle the click inside your ViewHolder. Change your code like this:
// use View.OnClickListener so that the click listener won't
// be recreated when the View is recycled
public class MyViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
public Button actionButton;
public ImageView img_book_thumbnail;
public ViewHolder(Context context, View itemView) {
super(itemView);
// find actionButton and img_book_thumbnail
...
// Attach a click listener to both of them
actionButton.setOnClickListener(this);
img_book_thumbnail.setOnClickListener(this);
}
// Handles clicked
#Override
public void onClick(View view) {
if(view.getId == R.id.action_button) {
// do something
} else if(view.getId == R.id.book_img_id) {
// do something
}
}
}
Instead of directly handling the click in Adapter, you should delegate the click handling to the parent activity / fragment of the Adapter by using a Listener/Callback mechanism.

Related

Android Jetpack Safe Args Issue

I have an app which has one main activity and 3 fragments. Let's name them A, B and C as shown below:
Fragment A has a RecyclerView. When i click on an item in the
RecyclerView, it passes an Account object in safe args to the
Fragment B where it displays the data.
In Fragment B, the user can then press the edit button to edit the Account object, which then
navigates him/her to Fragment C.
In fragment C, the user can either press the back button to cancel
the modification and return to Fragment B, or the user can modify and
press save to go back to Fragment A.
The problem here is that, if after pressing the edit button, the user perform some modifications and then press the back button without saving, it still modifies the object temporarily (temporarily because if i close the app and then open it again, the object resets to its original state.).
Below is my code (this is a simple code just for the sake of reproducing the issue):
Fragment A.kt
<androidx.constraintlayout.widget.ConstraintLayout
android:id="#+id/Layout_Fragment_Account"
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/RecyclerView_Account"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:visibility="#{ViewModel.loadingStatus==List.LIST ? View.VISIBLE : View.GONE}"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:visibility="gone" />
...
</androidx.constraintlayout.widget.ConstraintLayout>
Fragment A.kt
private fun subscribeAccounts(accounts: List<Account>) {
val adapter = AccountAdapter(
/* The click listener to handle account on clicks */
AccountClickListener {
navigateTo(AccountFragmentDirections.actionFragmentAccountToFragmentViewAccount(it))
},
/* The click listener to handle popup menu for each accounts */
AccountOptionsClickListener { view, Account ->
//View Popup
}
)
binding.RecyclerViewAccount.apply {
/*
* State that layout size will not change for better performance
*/
setHasFixedSize(true)
/* Bind the layout manager */
layoutManager = LinearLayoutManager(requireContext())
/* Bind the adapter */
this.adapter = adapter
}
/* Submits the list for displaying */
adapter.submitList(accounts)
}
Fragment B.xml
<layout 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">
<data>
<variable
name="Account"
type="...Account" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:id="#+id/Layout_Credential_View"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:id="#+id/Account_Logo"
errorResource="#{#drawable/ic_account_placeholder}"
imageUrl="#{Account.logoUrl}"
loadingResource="#{#drawable/ic_image_loading}"
android:layout_width="100dp"
android:layout_height="100dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:src="#drawable/ic_account_placeholder" />
<TextView
android:id="#+id/Account_Name"
style="#style/Locky.Text.Title5.Name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="32dp"
android:layout_marginTop="16dp"
android:layout_marginEnd="32dp"
android:text="#{Account.entryName}"
android:textAlignment="center"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/Account_Logo"
tools:text="This can be a very very very long title toooooo" />
...
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
Fragment B.kt
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
/* Binds the UI */
_binding = FragmentViewAccountBinding.inflate(inflater, container, false)
/* Instantiate the view model */
_viewModel = ViewModelProvider(this).get(ViewAccountViewModel::class.java)
/* Bind lifecycle owner to this */
binding.lifecycleOwner = this
/*
* Fetch the account object from argument
* Then bind account object to layout
*/
val account = ViewAccountFragmentArgs.fromBundle(requireArguments()).accountToVIEW
binding.account = account
_account = account
/* Returns the root view */
return binding.root
}
override fun onDestroyView() {
super.onDestroyView()
_binding = null
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setHasOptionsMenu(true)
}
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
inflater.inflate(R.menu.menu_credentials_actions, menu)
super.onCreateOptionsMenu(menu, inflater)
}
override fun onOptionsItemSelected(item: MenuItem) =
when (item.itemId) {
R.id.Action_Duplicate -> {
navigateTo(
ViewAccountFragmentDirections.actionFragmentViewAccountToFragmentAddAccount(
_account.apply {
this.id = 0
})
)
true
}
R.id.Action_Edit -> {
navigateTo(
ViewAccountFragmentDirections.actionFragmentViewAccountToFragmentAddAccount(
_account
)
)
true
}
else -> false
}
}
Fragment C.xml
<layout 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">
<data>
<import type="...Constants" />
<variable
name="ViewModel"
type="...AddAccountViewModel" />
</data>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:paddingStart="16dp"
android:paddingEnd="16dp"
android:paddingBottom="16dp">
...
<!--
**************** Require Text Fields ****************
-->
<com.google.android.material.textfield.TextInputLayout
android:id="#+id/Account_Name"
style="#style/Locky.TextBox.Default"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:hint="#string/field_account_name"
app:endIconMode="clear_text"
app:helperText="#string/label_required"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/Barrier_Logo"
app:startIconDrawable="#drawable/ic_profile">
<com.google.android.material.textfield.TextInputEditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="textCapWords"
android:text="#={ViewModel.entryName}" />
</com.google.android.material.textfield.TextInputLayout>
...
</LinearLayout>
</layout>
Fragment C.kt
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
_binding = FragmentAddAccountBinding.inflate(inflater, container, false)
/* Binds the UI */
_binding = FragmentAddAccountBinding.inflate(inflater, container, false)
/* Instantiate the view model */
_viewModel = ViewModelProvider(this).get(AddAccountViewModel::class.java)
/* Bind view model to layout */
binding.viewModel = viewModel
/* Bind lifecycle owner to this */
binding.lifecycleOwner = this
/*
* Fetch the account object from argument
* And set it to view model for two way binding
*/
val account = AddAccountFragmentArgs.fromBundle(requireArguments()).accountToADD
viewModel.setAccount(account)
/* Returns the root view */
return binding.root
}
Fragment C view model
class AddAccountViewModel(application: Application) : ObservableViewModel(application) {
/**
* Bindable two-way binding
**/
private lateinit var _account: Account
var entryName: String
#Bindable get() {
return _account.entryName
}
set(value) {
_account.entryName = value
notifyPropertyChanged(BR.entryName)
}
var logoUrl: String?
#Bindable get() {
return _account.logoUrl
}
set(value) {
_account.logoUrl = value ?: ""
notifyPropertyChanged(BR.logoUrl)
}
internal fun setAccount(account: Account?) {
this._account = account ?: Account()
}
}
navigation.xml
<fragment
android:id="#+id/Fragment_A"
android:name="...AccountFragment"
android:label="Accounts"
tools:layout="#layout/fragment_account">
<action
android:id="#+id/action_Fragment_Account_to_BottomSheet_Fragment_Account_Filter"
app:destination="#id/BottomSheet_Fragment_Account_Filter" />
<action
android:id="#+id/action_Fragment_Account_to_Fragment_View_Account"
app:destination="#id/Fragment_View_Account" />
</fragment>
<fragment
android:id="#+id/Fragment_B"
android:name="...ViewAccountFragment"
android:label="View Account"
tools:layout="#layout/fragment_view_account">
<action
android:id="#+id/action_Fragment_View_Account_to_Fragment_Add_Account"
app:destination="#id/Fragment_Add_Account" />
<argument
android:name="ACCOUNT_toVIEW"
app:argType="....Account" />
</fragment>
<fragment
android:id="#+id/Fragment_C"
android:name="...AddAccountFragment"
android:label="Add Account"
tools:layout="#layout/fragment_add_account">
<action
android:id="#+id/action_Fragment_Add_Account_to_BottomSheet_Fragment_Account_Logo"
app:destination="#id/BottomSheet_Fragment_Account_Logo" />
<argument
android:name="ACCOUNT_ToADD"
app:argType="...Account" />
</fragment>
Below is a demonstration of the issue:
I've tried a quick hack where i save an instance of an unmodified account object and before the user leaves the fragment i reset the changes by assigning each variable but this is not efficient. I think i am doing something wrong here with the safe args?
Can someone please help me. I really can't figure this one out. Thank you

Android: Attempt to invoke virtual method on a null object reference in onBindViewHolder

My app is supposed to parse JSON data and display it in a recycler view. When I run the app, it crashes with the following error:
I've re-checked my code multiple times to make sure I am calling the correct resource files, but I still cant find the source of the issue. The logcat tells me the issue is in the onBindViewHolder method in the recycler view adapter class, but everything looks normal to me. Can anyone point me in the right direction? Here is the relevant code:
Recycler View Adapter:
public class IndRvAdapter extends RecyclerView.Adapter<IndRvAdapter.MyViewHolder> {
private Context context;
private List<Ind> indList;
public IndRvAdapter(Context context, List<Ind> indList) {
this.context = context;
this.indList = indList;
}
#NonNull
#Override
public MyViewHolder onCreateViewHolder(#NonNull ViewGroup viewGroup, int i) {
View view;
LayoutInflater inflater = LayoutInflater.from(context);
view = inflater.inflate(R.layout.ind_card, viewGroup, false);
final MyViewHolder viewHolder = new MyViewHolder(view);
viewHolder.indView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Intent i = new Intent(context, SingleInd.class);
i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
i.putExtra("pacshort", indList.get(viewHolder.getAdapterPosition()).getPacshort());
i.putExtra("supopp", indList.get(viewHolder.getAdapterPosition()).getSuppopp());
i.putExtra("candname", indList.get(viewHolder.getAdapterPosition()).getCandname());
i.putExtra("district", indList.get(viewHolder.getAdapterPosition()).getDistrict());
i.putExtra("amount", indList.get(viewHolder.getAdapterPosition()).getAmount());
i.putExtra("party", indList.get(viewHolder.getAdapterPosition()).getExpParty());
i.putExtra("payee", indList.get(viewHolder.getAdapterPosition()).getPayee());
i.putExtra("date", indList.get(viewHolder.getAdapterPosition()).getDate());
i.putExtra("origin", indList.get(viewHolder.getAdapterPosition()).getOrigin());
i.putExtra("source", indList.get(viewHolder.getAdapterPosition()).getSource());
context.startActivity(i);
}
});
return viewHolder;
}
#Override
public void onBindViewHolder(#NonNull MyViewHolder myViewHolder, int i) {
myViewHolder.pacshorts.setText(indList.get(i).getPacshort());
myViewHolder.supOpp.setText(indList.get(i).getSuppopp());
myViewHolder.candName.setText(indList.get(i).getCandname());
myViewHolder.district.setText(indList.get(i).getDistrict());
}
#Override
public int getItemCount() {
return indList.size();
}
public class MyViewHolder extends RecyclerView.ViewHolder{
TextView pacshorts, supOpp, candName, district;
LinearLayout indView;
public MyViewHolder(#NonNull View itemView) {
super(itemView);
pacshorts = itemView.findViewById(R.id.tv_pacshort);
supOpp = itemView.findViewById(R.id.tv_suppopp);
candName = itemView.findViewById(R.id.tv_candname);
district = itemView.findViewById(R.id.tv_district);
indView = itemView.findViewById(R.id.ind_view);
}
}
}
Card View resource:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
xmlns:card_view="http://schemas.android.com/apk/res-auto"
android:orientation="vertical"
android:id="#+id/ind_view">
<android.support.v7.widget.CardView
android:layout_width="match_parent"
android:layout_height="250dp"
android:layout_gravity="center"
android:layout_margin="5dp"
android:elevation="3dp"
card_view:cardCornerRadius="15dp"
card_view:cardElevation="4dp"
card_view:cardUseCompatPadding="true">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#color/colorPrimaryDark">
<TextView
android:id="#+id/pacshort"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center"
android:text="TextView" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="#+id/pacshort"
android:id="#+id/tv_suppop"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="#+id/tv_candname"
android:layout_below="#+id/tv_suppop"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="#+id/tv_district"
android:layout_below="#+id/tv_candname"/>
</RelativeLayout>
</android.support.v7.widget.CardView>
</LinearLayout>
Recycler View:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#color/background_color"
tools:context=".IndExpend">
<android.support.v7.widget.RecyclerView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/ind_rv">
</android.support.v7.widget.RecyclerView>
</RelativeLayout>
Main Activity that parses the JSON data
public class IndExpend extends AppCompatActivity {
/*
* Open secrets API: Returns the 50 latest independent expenditures transactions
* Updated every 4 days
*/
private static final String url = "http://www.opensecrets.org/api/?method=independentExpend&output=json&apikey=d1ff8f708ca0745d75e9ffa0ee6f3d09";
private LinearLayoutManager linearLayoutManager;
private List <Ind> indList;
private RecyclerView myrv;
private RecyclerView.Adapter adapter;
public IndExpend(){}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_ind_expend);
linearLayoutManager = new LinearLayoutManager(this);
linearLayoutManager.setOrientation(LinearLayoutManager.VERTICAL);
myrv = findViewById(R.id.ind_rv);
indList = new ArrayList<>();
adapter = new IndRvAdapter(this, indList);
myrv.setHasFixedSize(true);
myrv.setLayoutManager(linearLayoutManager);
myrv.setAdapter(adapter);
getData();
}
private void getData() {
final ProgressDialog progressDialog = new ProgressDialog(this);
progressDialog.setMessage("Loading...");
progressDialog.show();
StringRequest stringRequest = new StringRequest(Request.Method.GET, url, new Response.Listener<String>() {
#Override
public void onResponse(String response) {
progressDialog.dismiss();
try {
JSONObject object = new JSONObject (response);
JSONObject responseObj = object.getJSONObject("response");
JSONArray array = responseObj.getJSONArray("indexp");
for (int i = 0; i < array.length(); i++){
JSONObject attributesObj = array.getJSONObject(i).getJSONObject("#attributes");
Ind ind = new Ind(attributesObj.getString("pacshort"),
attributesObj.getString("suppopp"),
attributesObj.getString("candname"),
attributesObj.getString("district"),
attributesObj.getString("amount"),
attributesObj.getString("party"),
attributesObj.getString("payee"),
attributesObj.getString("date"),
attributesObj.getString("origin"),
attributesObj.getString("source"));
indList.add(ind);
}
adapter = new IndRvAdapter(getApplicationContext(), indList);
myrv.setAdapter(adapter);
}catch (JSONException e) {
e.printStackTrace();
}
}
}, new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
Log.e("Volley", error.toString());
progressDialog.dismiss();
}
});
RequestQueue requestQueue = Volley.newRequestQueue(this );
requestQueue.add(stringRequest);
}
}
Can anyone help me find the source of the problem?
I think it is because you are trying to bind the view with this id:
pacshorts = itemView.findViewById(R.id.tv_pacshort);
But on the layout file the id for that element its just pacshort:
<TextView
android:id="#+id/pacshort"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center"
android:text="TextView" />
Generally on these type of null pointer exceptions when working on Android happens for one of two reasons. First, the res file that's being inflated is not the right one or, second, the id used to bind the views is misspelled or doesn't exist in that view.

A prompt for user input makes the app crashes

I coded a demo for prompt user input, and it always crashes when i click the Ok button on the prompt dialog, giving me this error log:
08-24 06:24:11.669 3275-3275/com.sano.tamer.prompuserinputdialog W/IInputConnectionWrapper: beginBatchEdit on inactive InputConnection
08-24 06:24:11.669 3275-3275/com.sano.tamer.prompuserinputdialog W/IInputConnectionWrapper: endBatchEdit on inactive InputConnection
08-24 06:24:19.019 3275-3275/com.sano.tamer.prompuserinputdialog D/AndroidRuntime: Shutting down VM
08-24 06:24:19.019 3275-3275/com.sano.tamer.prompuserinputdialog W/dalvikvm: threadid=1: thread exiting with uncaught exception (group=0x5c8d4160)
08-24 06:24:19.039 3275-3275/com.sano.tamer.prompuserinputdialog E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.sano.tamer.prompuserinputdialog, PID: 3275
java.lang.NullPointerException
at com.sano.tamer.prompuserinputdialog.MainActivity$1$2.onClick(MainActivity.java:48)
at android.support.v7.app.AlertController$ButtonHandler.handleMessage(AlertController.java:157)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:149)
at android.app.ActivityThread.main(ActivityThread.java:5252)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:515)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:794)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:610)
at dalvik.system.NativeStart.main(Native Method)
I am using a physical device running KitKat.
Here are the files:
prompts.xml:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:padding="10dp">
<TextView
android:id="#+id/tvUserInput"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#string/tv_message" />
<EditText
android:id="#+id/etUserInput"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="text">
<requestFocus/>
</EditText>
</LinearLayout>
activity_main.xml:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="#dimen/activity_vertical_margin"
android:paddingLeft="#dimen/activity_horizontal_margin"
android:paddingRight="#dimen/activity_horizontal_margin"
android:paddingTop="#dimen/activity_vertical_margin"
android:orientation="vertical"
tools:context="com.sano.tamer.prompuserinputdialog.MainActivity">
<Button
android:id="#+id/btnShowPrompt"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#string/button_label"/>
<EditText
android:id="#+id/etResult"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>
MainActivity.java:
package com.sano.tamer.prompuserinputdialog;
import android.content.Context;
import android.content.DialogInterface;
import android.support.v7.app.AlertDialog;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
public class MainActivity extends AppCompatActivity {
private Button btnShowPrompt;
private EditText etResult;
final Context context = this;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
btnShowPrompt = (Button) findViewById(R.id.btnShowPrompt);
etResult = (EditText) findViewById(R.id.etResult);
btnShowPrompt.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
// get prompts.xml view
LayoutInflater inflater = LayoutInflater.from(context);
View vUserPrompt = inflater.inflate(R.layout.prompts,null);
AlertDialog.Builder adBuilder = new AlertDialog.Builder(context);
// set prompts.xml to be the layout file of the alertdialog builder
adBuilder.setView(vUserPrompt);
final EditText etUserInput = (EditText) findViewById(R.id.etUserInput);
// setup a dialog window
adBuilder
.setCancelable(false)
.setPositiveButton("Ok", new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialogInterface, int i) {
// get user input and set it to result
etResult.setText(etUserInput.getText());
}
})
.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialogInterface, int i) {
dialogInterface.cancel();
}
});
// create an alert dialog
AlertDialog alertDialog = adBuilder.create();
alertDialog.show();
}
});
}
}

Why doesn't my Viewholder work?

So I'm creating a List for which I'm using my own implementation of the array adapter. I'm also using a viewholder for performance. But for some reason I get a nullpointer exception at this line:
holder.icon.setImageResource(R.drawable.no);
So I guess my holder isn't initialized? But I don't see how else my code should be?
public class NameViewAdapter extends ArrayAdapter<String>{
private String[] names;
private static class ViewHolder{
ImageView icon=null;
}
public NameViewAdapter(Context context, int textViewResourceId,String[] names) {
super(context, textViewResourceId, names);
this.names = names;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder;
if(convertView==null)
{
LayoutInflater li =
(LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
convertView = li.inflate(R.layout.row,parent,false);
holder=new ViewHolder();
holder.icon=(ImageView) convertView.findViewById(R.id.icon);
convertView.setTag(holder);
}
else
{
holder = (ViewHolder) convertView.getTag();
}
if (names[position].length() > 4)
holder.icon.setImageResource(R.drawable.ok);
else
holder.icon.setImageResource(R.drawable.no);
return convertView;
}
}
This is my XML-file:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent" android:layout_height="match_parent">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="#+id/imageView"
android:src="#drawable/ic_launcher"/>
<TextView
android:id="#+id/label"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</LinearLayout>
K so it seemed that the next line:
holder.icon=(ImageView) convertView.findViewById(R.id.icon);
should be changed to
holder.icon=(ImageView) convertView.findViewById(R.id.imageView);
cause that's the id the ImageView has in the xml file

QuickReturn with RecyclerView

I am trying to have a CheckBox (which is above the RecyclerView) as QuickReturn. The simple idea would be to hide the checkbox, when scroling up and make it visible, when scrolling down. (as in https://github.com/dbleicher/recyclerview-grid-quickreturn.)
And, it kind of works, but I have a strange flickering when scrolling.
Any thoughts on that?
MainActivity.java:
public class MainActivity extends ActionBarActivity {
private CheckBox mCheckbox;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mCheckbox = (CheckBox) findViewById(R.id.checkbox);
RecyclerView recyclerView = (RecyclerView) findViewById(R.id.recview);
LinearLayoutManager layoutManager = new LinearLayoutManager(this);
recyclerView.setLayoutManager(layoutManager);
recyclerView.setItemAnimator(new DefaultItemAnimator());
recyclerView.setOnScrollListener(new RecyclerView.OnScrollListener() {
#Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
// Simple check if moved vertically.
// React to scrolls of a minimum amount (3, in this case)
if (dy > 3) {
if (mCheckbox.getVisibility() == View.VISIBLE) {
mCheckbox.setVisibility(View.GONE);
}
} else if (dy < -3) {
if (mCheckbox.getVisibility() == View.GONE) {
mCheckbox.setVisibility(View.VISIBLE);
}
}
}
});
List<String> dataset = Arrays.asList("one", "two", "three", "four", "five");
CustomAdapter adapter = new CustomAdapter(dataset);
recyclerView.setAdapter(adapter);
}
public class CustomAdapter extends RecyclerView.Adapter<CustomAdapter.ViewHolder> {
private List<String> mDataset;
public CustomAdapter(List<String> dataset) {
mDataset = dataset;
}
#Override
public ViewHolder onCreateViewHolder(ViewGroup arg0, int arg1) {
View v = LayoutInflater.from(arg0.getContext())
.inflate(R.layout.address_card, arg0, false);
return new ViewHolder(v);
}
public void onBindViewHolder(ViewHolder viewHolder, int position) {
viewHolder.mText.setText(mDataset.get(position));
}
#Override
public int getItemCount() {
return mDataset.size();
}
public class ViewHolder extends RecyclerView.ViewHolder {
private TextView mText;
public ViewHolder(View v) {
super(v);
mText = (TextView) v.findViewById(R.id.cardText);
}
}
}
}
activity_main.xml:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context="${relativePackage}.${activityClass}" >
<CheckBox
android:id="#+id/checkbox"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Check me" />
<android.support.v7.widget.RecyclerView
android:id="#+id/recview"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:scrollbars="vertical" />
</LinearLayout>
adress_card.xml:
<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.CardView xmlns:card_view="http://schemas.android.com/apk/res-auto"
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/card_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="5dp"
android:elevation="#dimen/cardview_default_elevation"
card_view:cardCornerRadius="#dimen/cardview_default_radius" >
<TextView
android:id="#+id/cardText"
android:layout_width="wrap_content"
android:layout_height="100dp"
android:text="TextView" />
</android.support.v7.widget.CardView>
In your activity_main.xml, you have both the checkbox and RV in a LinearLayout. Whenever you set the checkbox to View.GONE, not only does it remove that view, but it has to recalculate the layout of the RV (and grow it to full_size). That's a lot of work on the main thread. Instead of a LinearLayout, add both items to a FrameLayout (RV first, then the Checkbox). This will make the checkbox float on-top of the RV, but the RV size will no longer change when you hide the checkbox. I suspect this will eliminate the flickering.
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="${relativePackage}.${activityClass}" >
<android.support.v7.widget.RecyclerView
android:id="#+id/recview"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scrollbars="vertical" />
<CheckBox
android:id="#+id/checkbox"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Check me" />
</FrameLayout>
Using Gravity settings, you can position the checkbox to where you want it to appear (in front of the RV). If you want it at the top, and you don't want it to obscure the top-most items in the RV (when it's scrolled to the top), the look at the ItemDecorator class (QRBarDecoration) in the same project you reference above.
Hope that helps.