I am replacing existing code by databinding. But I face a problem.
I have some layout files shared by more than one activity/fragment. E.g there is a layout file layout_sub used by SubFragmentA and its extending class SubFragmentB. And the data model used in these two fragment are not the same.
The code looks like following.
public class SubFragmentA extends Fragment {
private DataA dataA;
#Override
public View onCreateView(Bundle Bundle) {
View v = LayoutInflator.from(getActivity()).inflate(R.layout.shared_layout);
initView(v, dataA);
return v;
}
private void initView(view v, DataA dataA) {
// use dataA to init v
}
}
public class SubFragmentB extends Fragment {
private DataB dataB;
#Override
public View onCreateView(Bundle Bundle) {
View v = LayoutInflator.from(getActivity()).inflate(R.layout.shared_layout);
initView(v, dataB);
return v;
}
private void initView(view v, DataB dataB) {
// use dataB to init v
}
}
So far, I think using DataA and DataB in layout_sub file at the same time is not a good idea, because it would require a lot of redundant code to decide which object to be used.
Please share your ideas on this problem.
Finally, I got a solution. The databinding is used for MVVM pattern. That means one layout corresponds to one ViewModel. And the ViewModel contains every data for UI layout. So I should prepare one ViewModel for each layout file. And every fragment/activity should just handle the ViewModel.
Related
Now,I want to replace all view by my own AppCompatDelegate.
And override method createView so that I can update all the views in project.
Now I can do update like this:
public class UpdatedView extends View
in AppCompatDelegate :
public View createView(String name) {
if ("View".equals(name)) {
return UpdatedView();
}
}
Now, the problem I'm facing is that is it possible to create a view wrapper for all View. So that I can write like this
public class ViewWrapper extends View{
View realView;
#Override
public void methods() {
//override the method which is need, like ondraw, onClick
doUpdateThings();
realView.methods();
}
}
wrapper all views like this.
public View createView(String name) {
if ("View".equals(name)) {
return UpdatedViewWrapper(new View());
}
}
I want to know whether it's possible or not, and safe or not.
Someting added: it seems, I don't need the replace delegate but override onCreateView is enough.
I've got a RecyclerView backed by a Realm findAll(). I use a RealmChangeListener to notify the list about updates, and everything works remarkably well given the heavy use of the blunt instrument notifyDataSetChanged().
private RealmResults<Sale> allSales;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
....
// Update sales list whenever the AllSales result changes
allSales = getRealm().where(Sale.class).findAll();
allSalesListener = new RealmChangeListener<RealmResults<Sale>>() {
#Override
public void onChange(RealmResults<Sale> results) {
saleAdapter.notifyDataSetChanged();
}};
allSales.addChangeListener(allSalesListener);
....
However, I'd really like to have good MVVC structure, keeping all the Realm code in the ViewModel and out of my Fragments. The Realm examples don't do this. And probably for good reason -- I don't see an elegant way to notify the adapter appropriately of changes in the RealmResults. Databinding isn't there yet; it doesn't seem to support backing a RecyclerView with an ObservableCollection... and even if it did, a RealmResult isn't an ObservableCollection.
At this point, I'm thinking that I need to create a "ListChangedListener" interface in my Fragment, and manually maintain a collection of listeners for every List property in my ViewModel. But that seems like an awful lot of extra code just to maintain View/Model separation.
TLDR: I'm looking for an example of a Realm-backed ListView or RecyclerView with no Realm code whatsoever in the View code. Or even just reassurance that my custom "listener" interface is a good path forward.
UPDATE: I had somehow overlooked the RealmRecyclerViewAdapter. See my answer below.
The Realm library includes a RealmRecyclerViewAdapter base class, which I had somehow overlooked. No matter how good your MVVC intentions, the Adapter can't really be divorced from the model implementation, so it may as well be one that's intended for it.
Anyhow, it is very clean and compact. Do yourself a favour and review the example.
Here's a minimalist working implementation, with Android Databinding used for the row fields to make the Adapter and ViewHolder even cleaner and simpler:
private void setUpRecyclerView() {
// Called from your onCreateView(...)
recyclerView.setLayoutManager(new LinearLayoutManager(mainActivity));
recyclerView.setAdapter(new MyRecyclerViewAdapter(mainActivity, mainActivity.getDb().serialsRR));
recyclerView.setHasFixedSize(true);
}
public class MyRecyclerViewAdapter extends RealmRecyclerViewAdapter<Serial, MyRecyclerViewAdapter.SerialViewHolder> {
private final ActivityMain activity;
public MyRecyclerViewAdapter(ActivityMain activity, OrderedRealmCollection<Serial> data) {
super(activity, data, true);
this.activity = activity;
}
#Override
public SerialViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View itemView = LayoutInflater.from(parent.getContext())
.inflate(R.layout.serial_row, parent, false);
return new SerialViewHolder(itemView);
}
#Override
public void onBindViewHolder(SerialViewHolder holder, int position) {
SerialRowBinding rowBinding = holder.getBinding();
rowBinding.setSerial(getData().get(position));
}
class SerialViewHolder extends RecyclerView.ViewHolder implements View.OnLongClickListener {
#Getter SerialRowBinding binding;
public SerialViewHolder(View view) {
super(view);
binding = DataBindingUtil.bind(view);
}
}
}
I am using the android studio fragments sample. My goal is to set the value of EditText et1 in fragment1 by using code in fragment2. Also I want to update the ListView lv, that is in fragment1 by using code in fragment2.
The example does some back and forth sending of ARG_SECTION_NUMBER, but that is just text and won't work for a listview. What does not work is this
private View rvza, rvl, rvea;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
rvza = inflater.inflate(R.layout.fragment_zeit_und_aktion, container, false);
rvl = inflater.inflate(R.layout.fragment_liste, container, false);
rvea = inflater.inflate(R.layout.fragment_eintragen_und_aendern, container, false);
....
}
...
Button btnNeu = (Button) rvea.findViewById(R.id.btnNeu); //this line is somewhere...
What also doesn't work is this
new MainActivity().myfunction((ListView)rvl.findViewById(R.id.listView));
it gives an Attempt to invoke interface method 'int
android.widget.ListAdapter.getCount()' on a null object reference at
public void myfunction(ListView lv)
{
int count = lv.getAdapter().getCount();
So how do I communicate between fragments having one parent activity? Is there a helper class?
If you want to access any method or variable, that has an use by more than one child, make parent activity singleton and access the desired method or variable in child fragment. Also if you want to access anything within fragment from anther, don't forgot to make and init an object of that fragment in parent activity and use that object further to add or replace fragment in fragment manager. By this you have access to every child through the parent activity
How to make singleton? See below example.
public class SingletonExample {
// Static member holds only one instance of the
// SingletonExample class
private static SingletonExample singletonInstance;
// SingletonExample prevents any other class from instantiating
private SingletonExample() {
}
// Providing Global point of access
public static SingletonExample getSingletonInstance() {
if (null == singletonInstance) {
singletonInstance = new SingletonExample();
}
return singletonInstance;
}
public void printSingleton(){
System.out.println("Inside print Singleton");
}
}
There is an official guide on Android developers on how to communicate between fragments:
https://developer.android.com/training/basics/fragments/communicating.html
Basically, your fragment A will talk to the activity via an interface and the activity will look up fragment B and call some method on the fragment.
If you want to decouple things more, however, you could use something like otto event bus:
http://square.github.io/otto/
It can be used to communicate between different parts of your application without having to deal with the underlying details.
Hi guys I'm trying to access few methods and variables of fragment(containing a recycler view) from the recycler views adapter class.. Simplest way is to pass in the fragment reference along with the adapter which creating it. But I dont think passing the full adapter reference which creating the adapter is a good approach.
I'm using RxJava in my project and tried a lot of things with PublishSubject like creating a Subject in adapter, calling its onNext which an event is performed and subscribe to that subject in the fragment but it didnt work out..
So any good approach will be highly appreciated.
TIA...
I'd suggest to introduce EventBus in your app - pretty elegant way of communication between different components of the app.
Then it'd look like:
Fragment:
public class MyFragment extends Fragment {
private EventBus eventBus = EventBus.getDefault();
RecyclerViewAdapter viewAdapter;
#Override
public void onAttach(Context context) {
super.onAttach(context);
eventBus.register(this);
}
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_a, container, false);
if (viewAdapter == null) {
viewAdapter = new RecyclerViewAdapter(eventBus);
}
RecyclerView recyclerView = (RecyclerView)rootView.findViewById(R.id.recyclerView);
recyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
recyclerView.setAdapter(viewAdapter);
return rootView;
}
#SuppressWarnings("unused") // invoked by EventBus
public void onEventMainThread(final DataRefreshedEvent event) {
// Do something!
}
#Override
public void onDetach() {
eventBus.unregister(this);
super.onDetach();
}
}
Adapter:
public class RecyclerViewAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
EventBus eventBus;
public RecyclerViewAdapter(EventBus eventBus) {
this.eventBus = eventBus;
}
void sentSomethingToFragment() {
eventBus.post(new DataRefreshedEvent());
}
.....
}
Event: public final class DataRefreshedEvent {}
And just a note - with Dagger, it'd look even better.
I hope, it helps
My suggestion is go with interface approach.
1. Create one interface.
2. Fragment should implement that interface.
3. Pass that interface reference to the adapter.
4. Call the interface method from adapter
So that way you can communicate between fragment and adapter.
Current situation:
We have different userinterfaces, which are build as fragments, for example the MenuFragment:
public class MenuFragment extends Fragment implements Hideable, View.OnClickListener {
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
this.view = inflater.inflate(R.layout.menu, container, false);
}
#Override
public void hide() {
view.setVisibility(View.GONE);
}
#Override
public void show() {
view.setVisibility(View.VISIBLE);
}
}
Hideable:
public static interface Hideable {
void hide();
void show();
}
States are enums:
public enum InterfaceState {
STANDARD, TWO, THREE ;
private List<Hideable> visibleElements;
private void setVisibleElements(Hideable[] visibleElements) {
this.visibleElements = Arrays.asList(visibleElements);
}
}
Set them using:
InterfaceState.STANDARD.setVisibleElements(new Hideable[] { menuFragment });
where menuFragment is
menuFragment = (MenuFragment) activity.getFragmentManager().findFragmentById(R.id.menu_fragment);
Now i change the state calling changeToState:
public void changeToState(InterfaceState state) {
List<Hideable> hideList = new LinkedList<>();
for (Hideable e : this.currentState.visibleElements) {
if (!state.visibleElements.contains(e))
hideList.add(e);
e.hide();
}
List<Hideable> showList = new LinkedList<>();
for (Hideable e : state.visibleElements) {
if (!this.currentState.visibleElements.contains(e))
showList.add(e);
e.show();
}
The system seems to be buggy. Sometimes UI elements dont appear.
Questions:
Is this a good way to implement an UI? If not, what's a better way to do?
Does Android allocate space for a view, which has Visibility="GONE"? To use the upper mechanism, I need to define all UI elements (like MenuFragment menuFragment) directly on app start.
Any suggestions/improvements?
Thanks to all readers.
I always advocate the convention over configuration rule. Android has some nice conventions on how to design you user interface and you should s tick to them whenever possible.
No it does not. That is, View.GONE makes the view act as though it's not there (from the layouts point of view), where as View.INVISIBLE allocates the layout space needed for the view, but simply makes the view invisible. However, even with View.GONE, you'll still be able to get your view with findViewById().
I think that storing information in an enum like that is bad idea. You should find a different method that suits you (either put your list in an activity, in the Application class or something similar.)
EDIT: You can find some usefull information about the pattern on Wikipedia, and you can read more about the Android Design Guidelines here. There is also a nice document about the pattern here. You can also look at AOSP's code style guidelines (or, well, rules) as they provide some nice conventions on how to write code.