Sending changing Data from View to Fragment - android

I have a Activity and its Layout contains a CustomView in which I'm able to draw and I also got a Fragment in which I'll make a statistic about the used colors, stroke widths and one other variable. So I have to send the information to the Fragment.
How can I do that?
My first idea would be to make lists with the colors, strokes..., then adding every drawing to the color list it is made of, then getting the size of the list and at last sending it to the fragment.
Or I could make a variable and every time a color, stroke width and variable is added, the specific variable would increase, but here's the same problem that I don't know how to send the value to the Fragment.
The second problem is that this variable is changing, so if you made one small line in red and you go to the statisitc it should say 1 small red line. If you make another one, then it should say 2 small red lines, how can I receive the latest value of these values?

You can create Interface to communicate between View and Fragment. Read more about Java interfaces here
public class drawView extends View {
private OnColorChangedListener mListener;
//
public interface OnColorChangedListener {
public void colorChanged(int color);
}
public void setOnColorChangedListener(OnColorChangedListener listener){
mListener = listener;
//Call this method to implement this interface in your Fragment
}
#Override
public void onDraw(Canvas canvas){
if(mListener != null){
mListener.colorChanged(somecolor);
}
}
}
public class SomeFragment extends Fragment implements OnColorChangedListener {
public void onCreate(){
drawView v = new drawView();
v.setOnColorChangedListener(this);
}
//If your class claims to implement an interface, all methods defined by that interface must appear in its source code before the class will successfully compile.
public void colorChanged(int color){
//Do your stuff with your color
}
}

One potential solution is to use the Observer/Observable pattern. In this case, your CustomView is the Observable while your Fragment is the Observer. Create a Observer (or Listener interface) with an appropriate name, change the Fragment to implement this interface, and add a method to the CustomView to register the Fragment as a Listener. Now all you need to do is call the onXxx() method from your Listener interface in order to notify the Fragment that a line was drawn. The Fragment is then responsible for calculating the statistics you need.

Related

How to ccess items inside RecyclerView from parent fragment?

I'm trying to attach an onClickListener() method to an item which is inside a Recycler view. I know I can easily achive that by doing it from the RecyclerAdapter, but the goal of doing that is to show a custom dialog with some information that parent fragment contains, there are some ways to pass data, but I think that's better to attach the listener from fragment instead, and this way I can directly access the data.
I've tried to access from the fragment the way I use to do it from the adapter, with some modifications:
myRecyclerAdapter.myViewHolder.reportContainer.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Toast.makeText(getContext(),"Touch",Toast.LENGTH_SHORT).show();
}
});
But aparently the myViewHolder object it's not created yet by the time I try to use it, so I get the Java NullPointerException (F..$&##^$&^%, don't misunderstand me, I love it).
So, I need some help to do what I'm trying to, or some other good ideas to try, warning: I;m really trying to avoid passing data, except with maybe a ViewModel (don't know if I can), becouse it's a lot of fields to pass
This is fundamentally incorrect. The problem here is, there are multiple ViewHolders in the RecylerView. Which one do you want to attach it to? There would be n number of items and not all items will be rendered at the same time.
Instead of updating the ViewHolder, use a callback.
class MyAdapter extends RecyclerView.Adapter {
MyAdapterCallback callback = null;
....
#Override
public void onBindViewHolder(holder: ViewHolder, position: Int) {
holder.reportContainer.setOnClickListener { // You can set this in OnCreateViewHolder as well.
if (callback != null) {
callback.onClick();
}
}
}
}
interface MyAdapterCallback {
void onClick()
}
From your fragment,
myAdapter.callback = new MyAdapterCallback() {
#Override
public void onClick() {
// Access your fragment variables here.
}
}

Android change UI from adapter

In my activity I have one recyclerview and each item view contains buttons. I want to be able to change some UI elements and other things such as an Array of custom objects for the adapter itself in my activity from the recyclerview adapter. Until now I declared the needed views as static but I found out that it's a terrible practice.
Example: I have the following recyclerview that represents a cart filled with a custom viewholder, from an Array of custom "cart_product" objects. (One of this custom oject's proprieties is "quantity" - represented by the spinner). I want to be able to change the object's "quantity" property by changing the spinner's value from the adapter... How could this be done? And when all the products are removed from cart (by swiping & detected from adapter) I want to display a textvie
ScreenShot
You can use callbacks:
In adapter create an interface:
public interface EventHandler {
void handle(int position) // if u need know position. If no, just create method without params
}
Create an private instance of interface in adapter:
public class YourAdapter extends RecyclerView.Adapter<YourHolder> {
private EventHanlder handler;
}
Implement EventHanlder in activity:
public class Mainacitivity extends Activity implements YourAdapter.EventHandler {
//.....
#Override
void handle (int position) {
// TODO do whatever u want
}
}
Add EventHandler to constructor parameters:
public YourAdapter (List<YourObject> data, EventHandler handler) {
//....
this.handler = handler;
}
When you need to change UI call
handler.hanlde(position);
And, finally pass this when initializing adapter
adapter = new YourAdapter (data, this)
If u need something else (not position), just change signature of handle() method

BottomSheetDialog/BottomSheetDialogFragment — which to use and how?

I'm working on a Material design app. One feature I want to implement is some kind of a poll. When a user clicks an element of a list, the persistent bottom sheet dialog, which looks like this should show up:
Then, when user clicks any button, this dialog should go away and the modal bottom sheet dialog should show up, providing a user with more information about the list item which was clicked at the beginning. It looks like this:
I can't find any clear explanations about BottomSheetDialog and BottomSheetDialogFragment, and how to use them correctly, even after reading some information about AppCompat dialogs. So, my questions are:
In what way are they different and which one should I use for each
case?
How to get data in the activity about which button was pressed in the dialog?
Any links to the code of implementations or tutorials about using them?
Finally, I've found the solution and it works. Tell me if I'm doing something wrong. It basically works like DialogFragment from this guide, but I've done it a bit different.
1) Their difference is the same as it of DialogFragment and Dialog, and they both are modal. If you need persistent dialog, use BottomSheetBehaviour instead (I found out that both dialogs had to be modal in my app).
2) I have to answer the third question with some code first, and then it will be easy to answer the second one.
3) Create a new public class, which extends BottomSheetDialogFragment, I called it FragmentRandomEventPoll. There are two two things which have to be implemented here.
Override method onCreateView. It is nearly the same as onCreate method in Activities, except for that it returns the View it should inflate:
// We will need it later
private static String headerItem;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.fragment_random_event_poll, container, false);
header = (TextView) v.findViewById(R.id.uRnd_fragment_bottom_sheet_poll_header);
skip = (Button) v.findViewById(R.id.uRnd_fragment_bottom_sheet_button_skip);
header.setText(...);
// I implemented View.OnClickListener interface in my class
skip.setOnClickListener(this);
return v;
}
Static method which you can pass necessary data to and get new instance of this class (Probably I could have just used a regular constructor, I'll have to experiment with it a bit more). URandomEventListItem is the data model class.
public static FragmentRandomEventPoll newInstance(URandomEventListItem item) {
FragmentRandomEventPoll fragment = new FragmentRandomEventPoll();
headerItem = item.getHeader();
return fragment;
}
2) To get input events in activity or any other place, define an interface with necessary methods and create setter method for it's instance:
private PollButtonClickListener listener;
public void setListener(PollButtonClickListener listener) {
this.listener = listener;
}
public interface PollButtonClickListener {
void onAnyButtonClick(Object data)
}
And in the place you want to get your data ("dialog_event_poll" tag was specified in the layout):
FragmentRandomEventPoll poll = FragmentRandomEventPoll.newInstance(events.get(id));
poll.setListener(new FragmentRandomEventPoll.PollButtonClickListener() {
#Override
public void onAnyButtonClick(Object data) {
// Do what you want with your data
}
});
poll.show(getSupportFragmentManager(), "dialog_event_poll");
}
If there is anything unclear, my project files could be found on Github.
About handling events from DialogFragment/BottomSheetDialogFragment.
For applications with many activities, this method is great:
context as MyDialogFragmentListener
But I have a problem with an application with single activity and multiple fragments. Since there can be a lot of fragments, it seems like a very bad option to transfer all events to the necessary fragments through the main activity. Therefore, I decided to do this:
private inline fun <reified T> findListeners(): ArrayList<T> {
val listeners = ArrayList<T>()
context?.let {
if (it is T) listeners.add(it)
if (it is AppCompatActivity) {
it.supportFragmentManager.fragments.forEach { fragment ->
if (fragment is T) listeners.add(fragment)
fragment.childFragmentManager.fragments.forEach { childFragment ->
if (childFragment is T) listeners.add(childFragment)
}
}
}
}
return listeners
}
Code in DialogFragment:
private val listeners by lazy { findListeners<MyDialogFragmentListener>() }
Of course, fragments can contain as many other fragments as you like and probably need to be checked through recursion, but in my case this is superfluous.

android adapter data changed to notify host activity

This is a my use case. I have an activity with a listview and a textview. The textview is the sum of all numbers on the listview, the listview has all the numbers.I have a custom adapter for this case.
On each row of the listview, I have a button. This button will change the number on this row.
what I want to do is this:
when user clicks the button, the value on each row is changed - doable.
the sum on the textview also changes accordingly.
This is a simplified example. In reality, I also have a constraint that I have a data model to represent the data on each role. I am not able to extend the data model to DataSetObserver.
any help?
You should create a listener, which listens for touch on your increment button and inside it call a method of the class extending an interface you created.
To make it easy, in your adapter you will have:
public interface OnIncrementListener{
onNumberIncremented();
}
private OnIncrementListener mListener;
//This is inside getView method of your adapter
myButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
mListener.onNumberIncremented();
}
});
Then your activity
public class MyActivity extends Activity implements OnIncrementListener {
//inside onCreate
myAdapter.setOnIncrementListener(this);
//end onCreate
#Override
public void onNumberIncremented() {
//Change value of your TextView here
}
}

How do I call a fragment from inside a custom view

DogActivity is using a custom View. The custom view handles some logic and so has fields. When a particular field reaches a certain value, I want to start a fragment whose parent is DogActivity. How would I do that?
Is it advisable to put a callback inside a custom view so that it calls its parent activity? Or is there a simpler way?
When programming you should always look for consistency, i.e. look around you and see how similar stuff to what you want to do is done. The Android SDK makes heavy use of callback listeners, so they are the way to go here.
In fact we don't even need to know what kind of View your CustomView really is, we can build a general purpose solution. Don't forget to adapt/optimize according to your specific surroundings however. And think about abstraction and generalisation once you get to a point where all your Views are spammed with listeners!
You will need 3 things:
A listener interface
public interface OnCountReachedListener {
public void onCountReached();
}
A place to accept the listener and a place to alert the listener in your CustomView
public class CustomView extends View {
private int theCount;
private OnCountReachedListener mListener;
public void setOnCountReachedListener(OnCountReachedListener listener) {
mListener = listener;
}
private void doSomething() {
while (theCount < 100) {
theCount++;
}
// The count is where we want it, alert the listener!
if (mListener != null) {
mListener.onCountReached();
}
}
An implementation of the interface in your Activity
public class DogActivity extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
View myView = new CustomView();
myView.setOnCountReachedListener(new OnCountReachedListener() {
#Override
public void onCountReached() {
Log.w("tag", "COUNT REACHED!");
// START YOUR FRAGMENT TRANSACTION HERE
}
});
}
}
For further information look at the source code of the View class and all the On**XY**Listener interfaces in the Android SDK. They should give you plenty to think about
What is the type of the field? Is it an EditText? SeekBar? Depending on the View, you'll be able to specify different listeners/callbacks to determine when they have changed and if they've reached a certain threshold. I would attach these listeners within onCreate of DogActivity. When the threshold is reached, use a FragmentTransaction to add your Fragment as the child of a container View in DogActivity.

Categories

Resources