getActivity() from non activity class Kotlin - android

I would like to go from Fragment to an Activity by clicking on an item of Fragment's RecyclerView. RecyclerView adapter is made in separate class, so in that class in the part of setOnClickListener{} I have to write the Intent part.
holder.itemView.setOnClickListener{
val intent = Intent(MainActivity.getActivity(),DetailsActivtiy::class.java)
intent.putExtra("ImageUrl", uri)
MainActivity.getActivity().startActivity(intent)
}
The problem as you might already guessed is MainActivty.getActivity() .
I tried the approach from Fragment via onButtonClick instead of MainActivty.getActivity() I wrote only activity and it worked perfectly but it does not work in RecyclerView adapter because it is non activity class. How to solve my problem?
Best, Armen.

You don't actually need an Activity to launch an Intent, you need a Context. You can get the Context of the viewholder's itemView for example, like this:
holder.itemView.setOnClickListener {
val context = holder.itemView.context
val intent = Intent(context,DetailsActivtiy::class.java)
intent.putExtra("ImageUrl", uri)
context.startActivity(intent)
}
Alternatively, you could introduce a listener mechanism and notify the Fragment that contains the adapter that an item has been clicked, and have the Fragment launch the appropriate Intent.

Related

How to update recycler view (data from sqllite) without restarting the activity

I have a single activity app (MainActivity) with a dialog fragment. The DialogFragment adds data to the sqllite data base and the MainActivity displays the data in a recycler view. The problem I'm facing is that when I click the "add button" in the DialogFragment and dismisses it, the newly inserted data does not get displayed in the recycler view until the activity gets recreated.
How do I display new data without restarting the app. I have already added "recyclerViewAdapterObject.notifyDataSetChanged" in the DialogFragment class, but it's still not working!
You can solve it by following activity lifecycle method. You just override onResume() method. Put your "data fetching" code and set your adapter inside onResume() method and call onResume() after the data save and before the DialogFragment dismiss. This fix your problem.
But it is not recommended. You should learn to use ViewModel, LiveData.
Example:
override fun onResume() {
super.onResume()
findViewById<RecyclerView>(R.id.rvSocket).also { rv ->
var userList : List<User> = fatchDataFromSQLite()
rv.layoutManager = LinearLayoutManager(this)
rv.setHasFixedSize(true)
adapter = UserAdapter(userList)
rv.adapter = adapter
}
}
Then call onResume() method where you save your data by button click before dismiss your DialogFragment.

RecyclerView item click and open another recyclerview

enter image description hereI have almost 250 patent item which i want to show in a parent recyclerview. After item click of parent recyclerview it will show over hundreds of data under each parent item in another recycler view. How can i do it like this picture.
I would do it this way. Create an interface:
interface AdapterContract{
fun onListItemClick(id: String) //whatever the parameter is
}
When you instantiate the Adapter in your Fragment1, you can implement this interface:
class Fragment1 : Fragment(), AdapterContract{
...
//when instantiating the adapter:
private val adapter: MyAdapter by lazy{
MyAdapter(this //for the interface)
}
//override the method of Adapter contract
}
Your adapters constructor should look like this: MyAdapter(adapterContract: AdapterContract) : ListAdapter<MyAdapter.MyViewHolder>(Diff_Util_SOME_OBJECT)
Than in the adapters ViewHolder:
itemHolder?.setOnClickListener{
adapterContract.onListItemClick(someId)
}
Now in your override method in Fragment1:
override method onListItemClick(id: String){
//pass this id to the next opening fragment (like Bundles or navargs)
//init `Fragment2` which is going to have all items, that belong to `Fragment1` selected item
}
Load the data :)
I hope you are using a database or something. Should work with hard coded data structures as well, but just to be safe :).

How to handle click in a nested RecyclerView

Can someone explain the logic on how to handle this matter:
I have a fragment that after a WebSocket call inflates 2 Recyclerviews.
Child Recyclerview is nested to Parent Recyclerview and the parent adapter calls the child adapter.
I want to put an Interface for a click listener which handles the click in the Child Items in the Fragment.
Where should I put the interface and which class should implement it?
What you're trying to do has been done multiple times.
There are various approaches you can try, but in general, responsibilities would look something like this:
YourContext (Fragment/Activity)
Inflates a layout with a RecyclerView.
Instantiates YourAdapter
Subscribes, Requests, Waits, for your data and passes it onto YourAdapter.
Maintains an interface for click handling, like:
interface YourThingClickHandler {
fun onThingWasClicked(thing: Thing) // and any other thing you may need.
}
Can be YourContext: YourThingClickHandler or if you want, you can keep an anonymous/local instance of that. I usually do the former and then implement the fun onThingWasClicked(...) in the fragment/activity, it depends what you need to do when the item was clicked.
YourAdapter
Expects a list of Things and one YourThingClickHandler instance. So in your Fragment/Activity you'd do, something like (pseudo code):
// This is called once your ViewModel/Presenter/Repository/etc. makes the data available.
fun onThingsLoaded(things: List<Thing>) {
adapter.setClickHandler(this) // this can be passed when you construct your adapter too via constructor like adapter = YourAdapter(this)
adapter.submitList(things) // if the adapter extends a `ListAdapter` this is all you need.
}
Now that you've passed an outer click handler, you need to deal with the inner list. Now you have a few choices:
1. pass the same click handler all the way in and let the innerAdapter directly talk to this.
2. Have the outerAdapter act as an intermediate between the clicks happening in the innerAdapter and bubble them up via this click handler you just supplied.
Which one you chose, will depend largely on what you want to do with it, and how you want to handle it. There's no right or wrong in my opinion.
Regardless of what you do, you still need to get from the view holder to this click handler...
So in YourAdapter you should have another Interface:
interface InternalClickDelegate {
fun onItemTappedAt(position: Int)
}
This internal handler, will be used to talk from the viewHolder, back to your Adapter, and to bubble the tap up to the external click handler.
Now you can have a single instance of this, defined like so in your adapter class (remember this is Pseudo-Code):
private val internalClickHandler: InternalClickDelegate? = object : InternalClickDelegate {
override fun onItemTappedAt(position: Int) {
externalClickHandler?.run {
onThingWasClicked(getItem(position))
}
}
}
So if the external click handler (the YourThingClickHandler you supplied) is not null, then fetch the item from the adapter's data source, and pass it along.
How do you wire this internal handler with each view holder?
When you do onCreateViewHolder, have a ViewHolder that takes... you guessed, a InternalClickDelegate instance and so...
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
val inflater = LayoutInflater.from(parent.context)
// decide which viewHolder you're inflating... and...
return YourViewHolder(whateverViewYouInflate, internalClickHandler)
Now your ViewHolder(s) have a reference to this internal click handler...
so when you do onBindViewHolder(...) you probably call a common ViewHolder method of your choice, for example if your View holder can be of different types, you probably have an Abstract viewHolder with a fun bind(thing: Thing) method or similar that each concrete viewHolder subType will have to implement... in there, you'd do something like this:
override fun bind(thing: Thing) {
if (clickHandler != null) {
someViewYourViewHolderInflated.setOnClickListener(this) // this assumes your ViewHolder implements View.OnClickListener from the framework
}
}
Because your ViewHolder implements View.OnClickListener, you must implement the onClick method in it:
override fun onClick(v: View?) {
clickHandler?.onItemTappedAt(adapterPosition)
}
And this is how your ViewHolder, will receive the tap/click event from Android in the onClick method, if you supplied a click Handler (you did in the adapter onCreateViewHolder when you passed the internalClickHandler), it will simply bubble the tap, passing the position. adapterPosition is the Kotlin equivalent of calling getAdapterPosition() in a RecyclerView adapter.
TOO LONG, DIDN'T READ GRAPH
Fragment: ExternalClickListener -> passes an instance of it to the Adapter.
Adapter: Receives the ExternalClickListener, passes an InternalClickListener to each ViewHolder.
ViewHolder: Receives the internal Click Listener, sets itself as Clickable (either the entire itemView or just any widgets you want to make clickable, if you want the whole cell to be clickable, simply use itemView which is the "whole" view of the ViewHolder.
When the viewHolder's view is tapped, android calls the click listener's onClick method. In there, and because you are in a ViewHolder, you can do getAdapterPosition() and pass this to the internal click handler you received.
The Adapter then can transform that position back into data, and because you supplied an External clickListener, it can pass the actual item back to the external click listener.
Wait, but how about a NESTED RecyclerView.
There's nothing special about that, you simply need to provide the same mechanism, and keep passing things around. What you do or how many of these interfaces you have, depends entirely on what you're trying to achieve; like I said at the beginning, each solution is different and other factors must be taken into account when making architectural decisions.
In general, keep this thing in mind: Separation of Concerns: keep things small and to the point. For E.g.: it may seem crazy to have this double interface, but it's very clear what each does. The internal one, is simply concerned about a "tap" in a "view", and to provide the position in a list where said tap occurred.
This is "all" the adapter needs to fetch the data and make an informed guess at what item was truly tapped.
The fragment doesn't know (or care) about "positions", that's an Adapter's implementation detail; the fact that positions exist, is oblivious to the Fragment; but the Fragment is happy, because it receives the Thing in the callback, which is what most likely needs to know (if you needed to know the position for whatever reason, tailor and modify the externalCallback to have the signature of your choice.
Now replicate the "passing hands" from your OuterAdapter to your InnerAdapter, and you have done what you wanted to do.
Good luck!
1) You should put interface in child adapter and implement that in parent and then pass another one interface (long peocess)
2) Use local broadcast manager
you will add ClickListener in parent adapter and also add it in constructor of adapter
public interface HandleClickListener
{
void onItemClicked(int position, SurveysListModel surveysListModel);
}
Make an instance of your clicklistener and then on holderclick listner get the position of item and its value from your model list
holder.itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
handleClickListener.onItemClicked(position, yourModelList.get(position));
});
and get in to your activity like this making an instance of you adapter
adapter = new InstrumentsSearchAdapter(yourModelsList, activity.this, new SearchAdapter.HandleClickListener() {
#Override
public void onItemClicked(int position, Model listModel) {
instumentChild = listModel.getInstrument_title();
Intent intent = new Intent(Activity.this, Listing.class);
intent.putExtra("selectedQuestions", listModel.getQuestions());
startActivityForResult(intent, 5);
}
});
And if you want to go to parent recyclerview class implement onActivityResutlt method and get data back from child through intent and get that intent in onActivityResutlt method

How to pass data from clicked item in recyclerview to viewmodel and open new activity?

As I wrote, I'm looking the solution to that problem. How to correctly in MVVM in Android pass texts and id clicked an item to ViewModel and open new activity?
The new activity is a detail of item. So when I click on the item I want to display new activity with data from the clicked item and I need item id to edit the object in the item.
Using the RecyclerView.Adapter to populate your data, implement OnClickListener to viewHolder.
At onClick Method (overridden) you can type your code to start new activity,
lets say you have model class called Test, and array list called testList. then:
Intent intent = new Intent(mContext, DetailActivity.class);
intent.putExtra(KEY, testList.get(getAdapterPosition()));
mContext.startActivity(intent);
Note that getAdapterPosition() will return the position where you clicked, mContext is context passed to adapter.
You will need your model class to implement Parcelable to allow model to be transferred between activities.
https://developer.android.com/guide/components/activities/parcelables-and-bundles

Clicking a link in a TextView that is part of an adapter in android

I am designing a chatting app where I am required to make links clickable inside a TextView. I have used android:autoLink="all" in TextView to distinguish links/phones etc.
This works up to displaying the part of TextView that are links/phones as clickable but when I click it, I get the following exception:
android.util.AndroidRuntimeException: Calling startActivity() from outside of an Activity context requires the FLAG_ACTIVITY_NEW_TASK flag. Is this really what you want?
I searched a bit and it seems that I need to pass the activity context while inflating the TextView from XML. But how can I do that as i am inflating this view inside an adapter? This TextView is part of card view that is part of a recycler view in an adapter.
Instead of using layoutInflater.inflate() to inflate the view, I used LayoutInflater.from(parent.getContext()).inflate() and it worked.
Either use the Parent Activity to start a new activity from adapter or set the Intent flag.
For parent activity pass the Activity to Adapter's Construcutor and initialize it to use in the adatper like this.
in you MainActivity
Activity activity = this;
MyAdapter _adpater = new MyAdapter(....., activity);
in MyAdapter class
//constructor
Activity activity;
public MyAdapter (......, Activity _activity) {
........
this.activity= _activity;
}
now you can use this activity to start new activity.
activity.startActivity(new Intent(activity,target.class));
or you can try setting the flag with your current method.
context.startActivity(new Intent(context.getApplicationContext, target.class).setFlags(Intent.FLAG_ACTIVITY_NEW_TASK));

Categories

Resources