0

enter image description here

The title of the question doesnt fully explain my problem so allow me to elaborate. I have an app that parses JSON data from the Google Civic API into a recyclerview. One particular field is a TextView that stores a PDF url in a string. How do I set the TextView so it is clickable and opens the PDF preferably in app. I've looked up solutions and I've tried using WebView and PDFReader amongst others, but I couldnt find any solutions that mentioned how to do this from a recyclerview, so I'm not sure where to place this code. Since I am clicking on an item in the recyclerView, I thought I might need to add the code in the recyclerview adapter instead of the main class.

Here is the code for the main class: This class handles the JSON parsing

    public class VoterInformation extends AppCompatActivity  {
    private static final String url = "https://www.googleapis.com/civicinfo/v2/voterinfo?address=2613+Irvington+Ave+San+Bernardino+CA+92407&electionId=2000&officialOnly=false&returnAllAvailableData=false&fields=contests(referendumBallotResponses%2CreferendumBrief%2CreferendumConStatement%2CreferendumEffectOfAbstain%2CreferendumPassageThreshold%2CreferendumProStatement%2CreferendumSubtitle%2CreferendumText%2CreferendumTitle%2CreferendumUrl)&key=AIzaSyB1B5mEKHK8PHDiNcGQ5ZU3fPIH9KWxAcQ";
    private TextView refTitle, refSub, refUrl;
    private LinearLayoutManager linearLayoutManager;
    private List<Refs> refList;
    private RecyclerView myrv;
    private RecyclerView.Adapter adapter;


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

        refTitle = findViewById(R.id.ref_title);         //Initialize TextViews
        refSub = findViewById(R.id.ref_sub);


        myrv = findViewById(R.id.ref_rv);        //Initialize recycler view
        refList = new ArrayList<>();
        adapter = new RefRvAdapter(refList, getApplicationContext());

        linearLayoutManager = new LinearLayoutManager(this);
        linearLayoutManager.setOrientation(LinearLayoutManager.VERTICAL);

        myrv.setHasFixedSize(true);
        myrv.setLayoutManager(linearLayoutManager);
        myrv.setAdapter(adapter);

        TextView pdf_url = findViewById(R.id.ref_url);
        pdf_url.setClickable(true);
        pdf_url.setMovementMethod(LinkMovementMethod.getInstance());


        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 jsonObject = new JSONObject(response);
                    JSONArray array = jsonObject.getJSONArray("contests");
                    String pdf_url = array.getJSONObject(0).getString("referendumUrl") ;
                    for(int i = 0; i < array.length(); i++){
                        JSONObject jo = array.getJSONObject(i);
                        Refs refs = new Refs(jo.getString("referendumTitle"),
                                jo.getString("referendumSubtitle"),
                                jo.getString("referendumUrl"));
                        refList.add(refs);
                    }
                    adapter = new RefRvAdapter(refList, getApplicationContext());
                    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);

    }


}

Here is my code for the adapter class:

public class  RefRvAdapter extends RecyclerView.Adapter<RefRvAdapter.MyViewHolder>{
    private Context context;
    private List<Refs> refsList;

    public RefRvAdapter(){}

    public RefRvAdapter(List<Refs> refList, Context context){
        this.refsList = refList;
        this.context = context;
    }
    @NonNull
    @Override
    public RefRvAdapter.MyViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int i) {
        View view;
        LayoutInflater mInflater = LayoutInflater.from(context);
        view = mInflater.inflate(R.layout.ref_card, viewGroup, false);
        final MyViewHolder viewHolder = new MyViewHolder(view);
        viewHolder.view_containers.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Intent i = new Intent (context, SingleReferendum.class);
                i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                i.putExtra("referendumTitle", refsList.get(viewHolder.getAdapterPosition()).getRefTitle());
                i.putExtra("referendumSubtitle", refsList.get(viewHolder.getAdapterPosition()).getRefSub());
                i.putExtra("referendumUrl", refsList.get(viewHolder.getAdapterPosition()).getRefUrl());

                context.startActivity(i);
            }
        });
        return new MyViewHolder(view);
    }

    @Override
    public void onBindViewHolder(@NonNull RefRvAdapter.MyViewHolder myViewHolder, int i) {
        myViewHolder.refTitle.setText(refsList.get(i).getRefTitle());
        myViewHolder.refSub.setText(refsList.get(i).getRefSub());
        myViewHolder.refUrl.setText(refsList.get(i).getRefUrl());

    }

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


    public static class MyViewHolder extends RecyclerView.ViewHolder{
        TextView refTitle, refSub, refUrl;
        LinearLayout view_containers;
        public MyViewHolder(View itemView){
            super(itemView);
            refTitle = itemView.findViewById(R.id.ref_title);
            refSub = itemView.findViewById(R.id.ref_sub);
            refUrl = itemView.findViewById(R.id.ref_url);
            view_containers = itemView.findViewById(R.id.view_container_ref);

        }
    }
}

The app parses all the data just fine. I just want to make the url clickable. When the user clicks it, the pdf opens. Do I add the code in the RecyclerView adapter class, or in the main activity?

ANSWER: Edit the onBindViewHolder method like so.

@Override
    public void onBindViewHolder(@NonNull final RefRvAdapter.MyViewHolder myViewHolder, int i) {
        myViewHolder.refTitle.setText(refsList.get(i).getRefTitle());
        myViewHolder.refSub.setText(refsList.get(i).getRefSub());
        myViewHolder.refUrl.setText(refsList.get(i).getRefUrl());
        myViewHolder.view_containers.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Intent i = new Intent (context, SingleReferendum.class);
                i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                i.putExtra("referendumTitle", refsList.get(myViewHolder.getAdapterPosition()).getRefTitle());
                i.putExtra("referendumSubtitle", refsList.get(myViewHolder.getAdapterPosition()).getRefSub());
                i.putExtra("referendumUrl", refsList.get(myViewHolder.getAdapterPosition()).getRefUrl());

                context.startActivity(i);
            }
        });

        myViewHolder.refUrl.setOnClickListener(new View.OnClickListener(){
            @Override   //handle pdf clicks
            public void onClick(View view) {
                String refUrl = refsList.get(myViewHolder.getAdapterPosition()).getRefUrl();
                Intent intent = new Intent (Intent.ACTION_VIEW, Uri.parse(refUrl));
                intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                context.startActivity(intent);
            }
        });

    }
Gabriel
  • 346
  • 5
  • 24
  • You don't really want to open the PDFs from directly within the `Adapter`. Instead, set up your `Adapter` similar to what is shown in [this answer](https://stackoverflow.com/a/40584425), where clicking an item will call back to your `Activity`, and there you can put your logic for opening a given PDF, however you plan to do that. – Mike M. Jan 25 '19 at 02:54
  • How do I specify the onClick on the TextView though? This answer shows how to handle onClicks for entire recyclerView objects. I already have a method for that in my recycler view adapter and it works ok. – Gabriel Jan 25 '19 at 04:14
  • Do you need to handle clicks on the individual `TextView`s in each item? Are they going to do different things if you click on different `TextView`s within the item? I read your question as saying that just clicking an item as a whole would open a PDF. Rereading it now, though, I do believe I may have misunderstood. – Mike M. Jan 25 '19 at 04:19
  • Yes exactly! Right now clicking on the entire object will direct the user to a new activity showing the rest of the referendum information. I need to differentiate clicks between the entire object and just the TextView. Each TextView has a unique url that leads to the online PDF. Every time the user clicks on the TextView, it will open the pdf either in a web browser or in app. I havent decided yet, because I want to solve this step first. – Gabriel Jan 25 '19 at 04:25
  • Jiajia's answer looks close. He calls the onClickListener method on myViewHolder.refUrl in the onBindViewHolder method for this. Do you think this is the right approach? Keep in mind, the PDFs aren't hardcoded in teh TextViews. They are pulled from a JSON response. – Gabriel Jan 25 '19 at 04:28
  • Well, yeah, you could just set another `OnClickListener` on the `TextView`, like you did for `view_container_ref`. Really, though, you shouldn't be starting another `Activity` directly from the `Adapter`, either. That's something the parent `Activity` should be handling. It'll work, though, but you're then allocating two additional anonymous `OnClickListener` instances for every item, in addition to having stuff in that `Adapter` that's not really related to displaying `RecyclerView` items, which is really about all it should be doing. – Mike M. Jan 25 '19 at 04:41
  • Gotcha. So how do I specify the TextView as a clickable item without calling onClickListener on it? Can you post an answer showing your though process? – Gabriel Jan 25 '19 at 04:48
  • You'd still need to call `setOnClickListener()` on it. If you look at the linked example, though, you'll see that the `ViewHolder` itself is the `OnClickListener` – the `itemView.setOnClickListener(this);` line, and the `onClick()` method in `ViewHolder`. That's saves the additional anonymous `OnClickListener` instance that your example has, since the `ViewHolder` instance is handling it. You can do so similarly for the `TextView`, and then check `view.getId()` in `onClick()` to see what was clicked. I can't really post a full answer, atm, as I'm on my phone. – Mike M. Jan 25 '19 at 04:58
  • Im still a little confused on how to go about it this way. For now, I will mark Jiajias answer as the correct one, but I will continue to look into this as it is the correct way to do it. Thanks for your input. You were very helpful! – Gabriel Jan 25 '19 at 06:00

1 Answers1

1

If I didn't misunderstand what you mean,just modify methods:onCreateViewHolder and onBindViewHolder to:

@NonNull
@Override
public RefRvAdapter.MyViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int i) {
    View view;
    LayoutInflater mInflater = LayoutInflater.from(context);
    view = mInflater.inflate(R.layout.ref_card, viewGroup, false);
    final MyViewHolder viewHolder = new MyViewHolder(view);
    return new MyViewHolder(view);
}

@Override
public void onBindViewHolder(@NonNull RefRvAdapter.MyViewHolder myViewHolder, int i) {
    myViewHolder.refTitle.setText(refsList.get(i).getRefTitle());
    myViewHolder.refSub.setText(refsList.get(i).getRefSub());
    myViewHolder.refUrl.setText(refsList.get(i).getRefUrl());
    myViewHolder.itemView.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            Intent i = new Intent (context, SingleReferendum.class);
            i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
            i.putExtra("referendumTitle", refsList.get(myViewHolder.getAdapterPosition()).getRefTitle());
            i.putExtra("referendumSubtitle", refsList.get(myViewHolder.getAdapterPosition()).getRefSub());
            i.putExtra("referendumUrl", refsList.get(myViewHolder.getAdapterPosition()).getRefUrl());

            context.startActivity(i);
        }
    }
    myViewHolder.refUrl.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            String refUrl = refsList.get(myViewHolder.getAdapterPosition()).getRefUrl();
            //open pdf here.
        }
    });
}

Try it and feedback if it's OK.

JiajiaGu
  • 1,279
  • 14
  • 10
  • Sorry, I don't think we are on the same page. The onClick method I already had isn't for opening the PDFs. It has a different function. I need to add another one for the onclicks regarding the PDFs. I think calling the onClick listener on myViewHolder.refUrl is correct, but the code inside it needs to change to reflect opening a PDF – Gabriel Jan 25 '19 at 04:04
  • @Gabriel I have updated the code snipet,please check if it is what you want. – JiajiaGu Jan 25 '19 at 05:27
  • Yes! I believe we are on the same page now. The only thing is when I call the getRefUrl method it says it cannot resolve the method. – Gabriel Jan 25 '19 at 05:33
  • Also, I saw that you moved my existing onClick method from the onCreateViewHolder method into the onBindViewHolder method. Is this for a reason? – Gabriel Jan 25 '19 at 05:46
  • @Gabriel Updated again.Missing write.Writing code in the browser is a poor experience. – JiajiaGu Jan 25 '19 at 05:55