I am using mvvm and data binding on my project.I have a recycler view and make an adapter.my data shown in recycler view fine.Now i want to create item click listener.
So at the end of my adapter i create an interface like this:
interface MyClickListener {
void onItemClick(MyEntity myEntity);
}
after that i added this listener to constructor of my adapter:
public class MyRecyclerAdapter extends ListAdapter<MyEntity, MyRecyclerAdapter.ViewHolder> {
public MyClickListener clickListener;
public MyRecyclerAdapter(MyClickListener clickListener) {
super(MyEntity.DIFF_CALLBACK);
this.clickListener = clickListener;
}
#Override
public void onBindViewHolder(#NonNull ViewHolder holder, int position) {
MyEntity item = getItem(position);
holder.bind(item,clickListener);
}
and i send my callback to bind view xml :
private void bind(MyEntity item, MyClickListener clickListener) {
binding.setEntity(item);
binding.setClickListener(clickListener);
binding.executePendingBindings();
}
In xml:
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<data>
<variable
name="entity"
type="com.test.database.myEntity" />
<variable
name="clickListener"
type="com.test.home.MyClickListener" />
</data>
<androidx.cardview.widget.CardView
android:id="#+id/cardview_id"
android:layout_width="190dp"
android:layout_height="280dp"
android:layout_margin="5dp"
android:clickable="true"
android:foreground="?android:attr/selectableItemBackground"
app:cardBackgroundColor="#android:color/white"
app:cardCornerRadius="10dp"
app:cardElevation="5dp"
app:cardUseCompatPadding="true"
android:onClick="#{() -> clickListener.onItemClick(entity)}">
and i initialize this callback in my adapter:
MyRecyclerAdapter adapter = new MyRecyclerAdapter(myEntity -> {
Toast.makeText(getContext(), myEntity.getID(), Toast.LENGTH_SHORT).show();
});
But when i run application i got this error:
error: cannot find symbol class MyClickListener
What is my mistake?
I have to make class instead of interface and make a new object? There is no way to use interface ?
It's an issue related to access modifier of your interface. Try to change it public
public interface MyClickListener {
void onItemClick(MyEntity myEntity);
}
Related
I want to use data binding in a fragment to trigger all my button clicks in handler class. When I run the code below I get the following error.
D:\Coding\updatedGithub\AndroidHiveDataBindingFragments\app\build\generated\ap_generated_sources\debug\out\com\example\androidhivedatabindingfragments\DataBinderMapperImpl.java:10: error: cannot find symbol
import com.example.androidhivedatabindingfragments.databinding.FragmentUserBindingImpl;
^
symbol: class FragmentUserBindingImpl
location: package com.example.androidhivedatabindingfragments.databinding
I'm not sure if the problem is with how I'm passing the context to the Handler class or if I've done some wrong the Data binding.
UserFragment
#Nullable
#Override
public View onCreateView(#NonNull LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
FragmentUserBinding binding = DataBindingUtil.inflate(inflater, R.layout.fragment_user, container, false);
View view = binding.getRoot();
User user = new User();
user.setName("First Last");
user.setEmail("email#email.com");
binding.setUser(user);
MyClickHandlers handlers = new MyClickHandlers(getActivity());
binding.setHandlers(handlers);
return view;
}
MyClickHandlers
public class MyClickHandlers {
private static final String TAG = "MyClickHandlers";
Context context;
public MyClickHandlers(Context context) {
this.context = context;
}
public void myClick() {
Log.d(TAG, "myClick: ");
}
}
fragment_user
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:bind="http://schemas.android.com/apk/res-auto">
<data>
<variable
name="user"
type="com.example.androidhivedatabindingfragments.User" />
<variable
name="handlers"
type="com.example.androidhivedatabindingfragments.MyClickHandlers" />
</data>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".UserFragment"
android:orientation="vertical">
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Click ME"
android:id="#+id/btn_Click"
android:onClick="#{handlers::myClick}"/>
<include android:id="#+id/content"
layout="#layout/content_main"
bind:user="#{user}" />
</LinearLayout>
</layout>
Your onClick() of MyHandlers has to have the view you click as a parameter, so change it to be:
public void myClick(View view) {
Log.d(TAG, "myClick: ");
}
I guess that will solve the problem, also please instantiate the MyClickHandlers with the context instead of activity, as both are different things.
so in onCreateView(), add requireContext() instead of getActivity()
MyClickHandlers handlers = new MyClickHandlers(requireContext());
I have two fragments both AFragment and BFragment. The AFragment has a ListView. The BFragment has a Texview and a Button. The Afragment will change the fragment and putString to BFragment when the user clicked the item on ListView. The BFragment got the data from Afragment and display the date to TextView. How can I do?
The sample is like that
You can use libs https://github.com/greenrobot/EventBus . very easy!!!
EventBus in 3 steps
Define events:
public static class MessageEvent { /* Additional fields if needed */ }
Prepare subscribers: Declare and annotate your subscribing method, optionally specify a thread mode:
#Subscribe(threadMode = ThreadMode.MAIN)
public void onMessageEvent(MessageEvent event) {/* Do something */};
Register and unregister your subscriber. For example on Android, activities and fragments should usually register according to their life cycle:
#Override
public void onStart() {
super.onStart();
EventBus.getDefault().register(this);
}
#Override
public void onStop() {
super.onStop();
EventBus.getDefault().unregister(this);
}
Post events:
EventBus.getDefault().post(new MessageEvent());
You can use Interface and LocalBroadcastReceiver both for refreshing the Fragment.
Create one LocalBroadcastReceiver
LocalBroadcastManager.getInstance(context).registerReceiver(refreshFragment ,
new IntentFilter("refreshFragment"));
and its Method in BFragment
private BroadcastReceiver refreshFragment = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
// Do The Changes You Want To Refresh BFragment
}
};
And Than Call This LocalBroadcastReceiver in OnClick Event of the Button in AFragment.
Intent intent =new Intent("refreshFragment");
LocalBroadcastManager.getInstance(context).sendBroadcast(intent);
You can do this by below approach without using external libraries:
Assume you've a MainActivity which hosts the two fragments FragmetA (which has a ListView) and FragmentB (which has the TextView)
Here's the full scenario:
Create an interface # FragmentB which will be used by MainActivity to know when a list item is selected
MainActivity will register a listener to FragmentA by overriding onAttachFramemnt() and implementing this interface
When the user selects a list item from FragmentA; FragmentB will trigger the callback back to MainActivity
MainActivity will forward the trigger to a public method in FragmentB which is used to set the text of the TextView to the current item.
Here is the code:
1. Layout
1.1 activity_main
<?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:orientation="vertical"
tools:context=".MainActivity">
<fragment
android:id="#+id/fragment_b"
android:name="com.example.android.sendingdatafromfragmenta_to_fragmentb.FragmentB"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<fragment
android:id="#+id/fragment_a"
android:name="com.example.android.sendingdatafromfragmenta_to_fragmentb.FragmentA"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>
1.2 fragment_a
<?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">
<ListView
android:id="#+id/listView"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>
1.3 fragment_b
<?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">
<TextView
android:id="#+id/tvSelectedItem"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="No Item selected" />
</LinearLayout>
2. Java
2.1 MainActivity
public class MainActivity extends AppCompatActivity implements FragmentA.OnListItemClickListener {
private static final String LOG_TAG = "LOG_TAG";
private FragmentB mFragmentB;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// inflating fragment B from xml
mFragmentB = (FragmentB) getSupportFragmentManager().findFragmentById(R.id.fragment_b);
}
#Override
public void onAttachFragment(Fragment fragment) {
Log.i(LOG_TAG, "onAttachFragment");
super.onAttachFragment(fragment);
if (fragment instanceof FragmentA) {
((FragmentA) fragment).setOnListItemClickListener(this);
}
}
#Override
public void onListItemClick(String selectedITem) {
mFragmentB.setSelectedITemText(selectedITem);
}
}
2.2 FragmentA
public class FragmentA extends Fragment {
#Nullable
#Override
public View onCreateView(#NonNull LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_a, container, false);
final String[] data = {"item 1", "item 2", "item 3", "item 4", "item 5"};
ListView listView = view.findViewById(R.id.listView);
ArrayAdapter adapter = new ArrayAdapter(requireContext(), android.R.layout.simple_list_item_1, data);
listView.setAdapter(adapter);
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
if (mOnListItemClickListener != null) {
mOnListItemClickListener.onListItemClick(data[position]);
}
}
});
return view;
}
interface OnListItemClickListener {
void onListItemClick(String selectedITem);
}
OnListItemClickListener mOnListItemClickListener;
public void setOnListItemClickListener(OnListItemClickListener listener) {
mOnListItemClickListener = listener;
}
}
2.3 FragmentB
public class FragmentB extends Fragment {
View view;
#Nullable
#Override
public View onCreateView(#NonNull LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
view = inflater.inflate(R.layout.fragment_b, container, false);
return view;
}
public void setSelectedITemText(String text) {
TextView selectedItemTextView = view.findViewById(R.id.tvSelectedItem);
selectedItemTextView.setText(text);
}
}
Results
<FrameLayout
android:id="#+id/content_frame"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:foreground="#drawable/window_foreground">
<android.support.design.widget.AppBarLayout
android:id="#+id/appbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:elevation="#dimen/appbar_resting"
android:theme="#style/AppTheme.NoActionBar">
<com.ujjwal.univhub.components.SearchView
android:id="#+id/search_bar"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</android.support.design.widget.AppBarLayout>
<include layout="#layout/fragment_university_lsit"></include>
</FrameLayout>
app_bar_main.xml
Here searchView is customFrameLayout.I used this xml file in my main activity xml file.
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<include
layout="#layout/app_bar_main"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<include
layout="#layout/floating_action_group"/>
</FrameLayout>
This is activity_main.xml
public class SearchView extends FrameLayout implements AdapterView.OnItemClickListener{
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
SuggestionAdapter.ViewHolder viewHolder = (SuggestionAdapter.ViewHolder)view.getTag();
University selectedUniversity = viewHolder.getUniversity();
String sendData = FilterBuilder.createCodeFilter(selectedUniversity.getCode()).toJson();
//may occure error here
Log.d("serach by name", "onItemClick: error");
KeyListener listener = new KeyListener((BaseActivity)getContext());//error thrown here.
listener.onClicked(sendData, Properties.LOCALHOST + Properties.UNIVERSITY_CODE);
queryInput.setText(selectedUniversity.getName());
suggestionAdapter.clear();
suggestionAdapter.notifyDataSetChanged();
}
}
In my MainActivity
setContentView(R.layout.activity_main);
java.lang.ClassCastException: android.view.ContextThemeWrapper cannot be cast to com.ujjwal.univhub.BaseActivity
My can't i cast context to the activity hosting the view.
ClassCastException simply means the object isn't of the type (or can't safely behave as the type) you are trying to cast into.
This might help.
If you create the view programatically providing an explicit context (your BaseActivity) using constructor new SearchView(yourBaseActivityContext), this exception should not be thrown. However, from xml, I'm not sure if the LayoutInflater uses your activity context or a more generic context for constructing the view.
Edit: (in response to op's comment)
i need it to post the data in UI through the handler of baseActivity. KeyListener will have to get access to BaseActivity instance. If i
have to make keylistener independent of activity then its will require
a complete turn over of the implementation.
In your BaseActivity, make an interface:
public interface KeyClickListener { void clicked(SomeUiData data) }
add a method to your SearchView class to register a listener (e.g. your BaseActivity) which can be notified
private BaseActivity.KeyClickListener mListener;
public void setKeyClickListener(BaseActivity.KeyClickListener listener) {
mListener = listener;
}
in your BaseActivity's onCreate() (so that listener is registered before any user clicks can be performed):
mSearchView = findViewById... ;// get your SearchView instance
mSearchView.setKeyclickListener(new KeyClickListener() {
void clicked(SomeUiData newUiInfo) {
// update your UI using newUiInfo here
// use any handler accessible in your BaseActivity
}
});
So, effectively, now you have a listener which is declared in your BaseActivity which can perform any UI update actions without KeyListener needing BaseActivity instance. Pass mListener to your KeyListener and in your original KeyListener.onClicked() method, you could invoke any methods you declared in KeyClickListener with the click/ui data which can update the UI (because it is declared in the activity).
I have two recyclerViews (say A and B) in the same layout that have an adapter which for both the recyclerViews look like this:
public class ChapterListAdapter extends RecyclerView.Adapter<ChapterListAdapter.ChapterListViewHolder>{
private final ArrayList<ChapterObj> mChapterListObj;
private final Context mContext;
public ChapterListAdapter(ArrayList<ChapterObj> chapterObj, Context c) {
mChapterListObj = chapterObj;
mContext = c;
}
#Override
public ChapterListViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.chapter_list_item, parent, false);
ChapterListViewHolder vh = new ChapterListViewHolder(v);
return vh;
}
#Override
public void onBindViewHolder(ChapterListViewHolder holder, int position) {
holder.chapterNumber.setText(mChapterListObj.get(position).getChapterNumber());
}
#Override
public int getItemCount() {
return mChapterListObj.size();
}
public class ChapterListViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener, View.OnTouchListener {
private TextView chapterNumber;
public ChapterListViewHolder(View itemView) {
super(itemView);
chapterNumber = (TextView) itemView.findViewById(R.id.chapter_number);
itemView.setOnClickListener(this);
itemView.setOnTouchListener(this);
}
#Override
public void onClick(View v) {
Log.d("hello", "hello");
}
}
}
}
Both A and B have click listeners. Independently, both work fine. But when both are in the same layout, whenever I click on an item of 'A', the click listener of 'B' gets triggered.
If you need to see more code, tell me which file you want to see, I'll add their code too.
Edit: The xml layout file in which I've used them together looks like this:
<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"
tools:context=".VerseActivity">
<android.support.v7.widget.RecyclerView
android:id="#+id/chapter_list_menu"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_alignParentLeft="true"
xmlns:android="http://schemas.android.com/apk/res/android"
>
</android.support.v7.widget.RecyclerView>
<android.support.v7.widget.RecyclerView
android:id="#+id/verse_list_menu"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:layout_alignParentTop="true"
xmlns:android="http://schemas.android.com/apk/res/android"
>
</android.support.v7.widget.RecyclerView>
</RelativeLayout>
You can do same thing with Sectioned recycler view. which will even load faster and let you display as many sectioned as you want. (removes hurdle of 2-3 recyclerview in same page or view)
If you want to create your own you can check this - https://gist.github.com/gabrielemariotti/4c189fb1124df4556058
or you can find many libraries for that also.
This question already has an answer here:
Android System services not available to Activities before onCreate() [closed]
(1 answer)
Closed 10 years ago.
How could I add items from listView(on my layout) from array?
I tried multiple times, but everytime I get error: System services not avaliable before onCreate.();. Yes, I. know that this er.ror is shown when I try to access ArrayAdapter. Tried almost everything. .Full code is here: pastebin.com/Nv5BkcS7
UPDATE
I followed some other tutorials. My code works now, but there is no data. http://pastebin.com/D8UFKC7i and xml http://pastebin.com/mJfwjGcB
Could someone tell me why it doesn't work.? Debugger says that arrays have all entires
Your onCreate() method is blank in the addcontacts activity, thats the problem:
public class addcontacts extends ListActivity {
protected void onCreate() {
//set content here
setContentView(R.layout.activity_add_contact);
}
...
...
And Don't forget to create activity_add_contact.xml in Layout folder
with content :
<?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" >
<ListView
android:id="#android:id/list"
android:layout_width="match_parent"
android:layout_height="wrap_content" >
</ListView>
</LinearLayout>
1.create a custom Adapter exteding BaseAdpter or ArrayAdpter and pass array or ArrayList in constructor.
2.Create the View in layout (of row )
3.inflate this xml in getview function of custom Adapter and set the data.
Activity XML
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
>
<ListView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:id="#+id/lstText"
/>
</LinearLayout>
list row XML (in layout row.xml)
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent" android:layout_height="fill_parent">
<LinearLayout
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:id="#+id/txtAlertText" />
</LinearLayout>
</LinearLayout>
Create Adapter Class inside your activity
class JSONAdapter extends BaseAdapter implements ListAdapter {
private final Activity activity;
private final JSONArray jsonArray;
private JSONAdapter (Activity activity, JSONArray jsonArray) {
assert activity != null;
assert jsonArray != null;
this.jsonArray = jsonArray;
this.activity = activity;
}
#Override public int getCount() {
if(null==jsonArray)
return 0;
else
return jsonArray.length();
}
#Override public JSONObject getItem(int position) {
if(null==jsonArray) return null;
else
return jsonArray.optJSONObject(position);
}
#Override public long getItemId(int position) {
JSONObject jsonObject = getItem(position);
return jsonObject.optLong("id");
}
#Override public View getView(int position, View convertView, ViewGroup parent) {
if (convertView == null)
convertView = activity.getLayoutInflater().inflate(R.layout.row, null);
TextView text =(TextView)convertView.findViewById(R.id.txtAlertText);
JSONObject json_data = getItem(position);
if(null!=json_data ){
String jj=json_data.getString("f_name");
text.setText(jj);
}
return convertView;
}
}
Then add this in your activity.
public class main extends Activity {
/** Called when the activity is first created. */
ListView lstTest;
//Array Adapter that will hold our ArrayList and display the items on the ListView
JSONAdapter jSONAdapter ;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
//Initialize ListView
lstTest= (ListView)findViewById(R.id.lstText);
jSONAdapter = new JSONAdapter (main.this,jArray);//jArray is your json array
//Set the above adapter as the adapter of choice for our list
lstTest.setAdapter(jSONAdapter );
}
And you are done.