I think I am making a design error in my Android app somewhere. My (simplified) code is pasted below.
I am using the writeMidi method in MainActivity. However, I would also like to use it, or actually just trigger it, when "onItemSelected" is triggered in the custom listener.
I am a bit torn on how to do that. Should I redesign this code to fit the customlistener in the main activity?
Thanks for any help.
public class MainActivity extends Activity{
int song = 0;
int[] music;
public int instrument;
public CustomOnItemSelectedListener listener;
// *******************************************************
// set Layout - on create
// *******************************************************
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
instrument = 0;
listener = new CustomOnItemSelectedListener();
addListenerOnSpinnerItemSelection();
//more stuff, including using the writeMidi method
};
public void addListenerOnSpinnerItemSelection(){
instrumentSp = (Spinner) findViewById(R.id.instrument);
instrumentSp.setOnItemSelectedListener(listener);
}
public void writeMidi(int[] music, int count) {
// so some stff
}
}
and in a separate file;
public class CustomOnItemSelectedListener implements OnItemSelectedListener {
private int instrument = 0;
public void onItemSelected(AdapterView<?> parent, View view, int pos,long id) {
Toast.makeText(parent.getContext(),
"Please wait a minute for the instrument to be changed. ", Toast.LENGTH_SHORT).show();
instrument = pos;
}
public int getInstrument(){
return instrument;
}
}
Use broadcast receiver in main class and send different type of broadcast(Different messages) to activate different methods in main activity.
You could create a Listener interface 'InstrumentSelectedListener', or something like that. Then have your MainActivity implement that interface, register it as a listener on your CustomOnItemSelectedListener, and fire a 'writeMidiNow' event in your onItemSelected.
You would end up with something like:
public class MainActivity extends Activity implements OnInstrumentSelectedListener{
int song = 0;
int[] music;
public int instrument;
public CustomOnItemSelectedListener listener;
// *******************************************************
// set Layout - on create
// *******************************************************
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
instrument = 0;
listener = new CustomOnItemSelectedListener();
addListenerOnSpinnerItemSelection();
//more stuff, including using the writeMidi method
};
public void addListenerOnSpinnerItemSelection(){
instrumentSp = (Spinner) findViewById(R.id.instrument);
instrumentSp.setOnItemSelectedListener(listener);
}
public void onInstrumentSelected(int instrument) {
// do some stuff with the instrument.
}
public void writeMidi(int[] music, int count) {
// so some stff
}
}
And
public class CustomOnItemSelectedListener implements OnItemSelectedListener {
public interface OnInstrumentSelectedListener{
public void onInstrumentSelected(int instrument);
}
private int instrument = 0;
private OnInstrumentSelectedListener instrumentlistener;
public void onItemSelected(AdapterView<?> parent, View view, int pos,long id) {
Toast.makeText(parent.getContext(),
"Please wait a minute for the instrument to be changed. ", Toast.LENGTH_SHORT).show();
instrument = pos;
if(instrumentListener != null)
instrumentListener.onInstrumentSelected(instrument);
}
public void setInstrumentListener(OnInstrumentSelectedListener listener) {
this.instrumentListener = listener;
}
public int getInstrument(){
return instrument;
}
}
2 ways to do it:
- First will be passing the context of MainActivity class to CustomOnItemSelectedListener class.
- Second way is quick and dirty, make the writeMidi() method as static, but you should keep in mind that static methods can access only static members, Not non-static members.
I tried a number of the solutions suggested, but could get none of them to fully work.
So I solved it by not using a separate class like this:
instrumentSp.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
public void onItemSelected(AdapterView<?> parent, View view, int pos,long id) {
Related
Any time I insert an item into my Room database, my recycler view flashes the row containing the newly inserted view holder (the rest of the list does not flash). I am using LiveData to keep my list automatically updated by calling submitList(list) in onChanged() of the observed ViewModel method. My adapter extends ListAdapter and I am using DiffUtil to track changes in the list. That being said, I don't call notifyItemInserted(position) directly as DiffUtil should do this for me. There are 2 instances where an item gets inserted (1) a completely new item gets inserted at the end of the list (2) a deleted item gets reinserted into the list. Both cases the item will insert itself then flash. I have read numerous posts where people suggest disabling animations on the recycler view but this is not an option for me as I rely on animations elsewhere in my code. Any other suggestions would be appreciated. I'm trying to keep the posted code brief but I can post more if it would be helpful.
MainActivity.java
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initComponents();
initRecyclerView();
setListeners();
setListObserver();
createItemTouchHelper();
}
private void setListObserver() {
viewModel.getAllItems().observe(this, new Observer<List<ListItem>>() {
#Override
public void onChanged(List<ListItem> newList) {
adapterMain.submitList(newList);
}
});
}
...
// Inserts a new ListItem when MainActivity's EditText is used
public void onClick(View v) {
if (v.getId() == R.id.img_add_item_main) {
String itemName = String.valueOf(edtAddItem.getText());
if (!itemName.trim().equals("")) { // Insert new list item only if the EditText is not empty
ListItem item = new ListItem();
item.setItemName(itemName);
viewModel.insert(item);
}
...
// SnackBar to allow a user to undo a delete operation
public void showUndoSnackBar(ListItem deletedItem) {
Snackbar undoSnackBar = Snackbar.make(constraintLayout, "Undo deleted Item",
Snackbar.LENGTH_LONG).setAction("UNDO", new View.OnClickListener() {
#Override
public void onClick(View v) {
// Restore deleted item to its original position in the list if UNDO is clicked
viewModel.insert(deletedItem);
}
});
undoSnackBar.show();
}
RecyclerAdapterMain.java
public class RecyclerAdapterMain extends ListAdapter<ListItem, RecyclerAdapterMain.ListItemHolder> {
public RecyclerAdapterMain() {
super(DIFF_CALLBACK);
}
private static final DiffUtil.ItemCallback<ListItem> DIFF_CALLBACK = new DiffUtil.ItemCallback<ListItem>() {
#Override
public boolean areItemsTheSame(#NonNull ListItem oldItem, #NonNull ListItem newItem) {
return oldItem.getId() == newItem.getId();
}
#Override
public boolean areContentsTheSame(#NonNull ListItem oldItem, #NonNull ListItem newItem) {
return oldItem.equals(newItem);
}
#Override
public ListItemHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View itemView = LayoutInflater.from(parent.getContext())
.inflate(R.layout.recycler_item_layout_main, parent, false);
return new ListItemHolder(itemView);
}
#Override
public void onBindViewHolder(#NonNull ListItemHolder holder, int position) {
ListItem item = getItem(position);
holder.txtItemName.setText(item.getItemName());
holder.checkBox.setChecked(item.getIsChecked());
if(item.getIsChecked()) {
holder.txtItemName.setTextColor(Color.LTGRAY);
} else {
holder.txtItemName.setTextColor(Color.BLACK);
}
}
...
ListItem.java (POJO)
#Entity(tableName = "list_item_table")
public class ListItem {
#PrimaryKey(autoGenerate = true)
private long id;
private String itemName;
private boolean isChecked;
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
... other getters and setters
public boolean equals(#Nullable ListItem listItem) {
return this.itemName.equals(listItem.getItemName()) && this.isChecked == listItem.getIsChecked();
}
}
I determined that the DefaultItemAnimator class (which is the animator for RecyclerView at the time of this writing) has a method called animateAdd(final RecyclerView.ViewHolder holder) that sets the holder's alpha to 0 then animates it to 1 over time. I verified that this was the cause of the blinking by changing the default setting to 1. I solved the problem by a using a combination of the accepted answer from this StackOverflow post and the documentation for DefaultItemAnimator.
First, I created a new animator class that extends DefaultItemAnimator in order to override the animateAdd() method. Under the section for animateAdd(), the documentation states, "Called when an item is added to the RecyclerView. Implementors can choose whether and how to animate that change, but must always call dispatchAddFinished(RecyclerView.ViewHolder) when done, either immediately (if no animation will occur) or after the animation actually finishes." I called dispatchAddFinished() immediately to avoid the add animation. Instead of disabling animations altogether, all animations are present except for the problematic one.
MyRecyclerViewAnimator.java
public class MyRecyclerViewAnimator extends DefaultItemAnimator {
#Override
public boolean animateAdd(RecyclerView.ViewHolder holder) {
dispatchAddFinished(holder); // this is what bypasses the animation
return true;
}
/* this is the default implementation of animateAdd() in DefaultItemAnimator
#Override
public boolean animateAdd(final RecyclerView.ViewHolder holder) {
resetAnimation(holder);
holder.itemView.setAlpha(0); // this is what caused the flashing/blinking
mPendingAdditions.add(holder);
return true;
}
*/
}
MainActivity.java
...
recyclerMain.setItemAnimator(new MyRecyclerViewAnimator()); // set the default animator to your extended class
...
I am using custom recycler view and in adapter class i have implemented interface which is always null on button click. Here is my adapter class.
public class FeedListAdapter extends
RecyclerView.Adapter<AddtoCartHolder> {
private OnFeedItemClickListener onFeedItemClickListener;
public FeedListAdapter(Activity activity, ArrayList<CartItem> feedItems) {
this.activity = activity;
this.feedItems = feedItems;
this.filteredfeedItems = feedItems;
inflater = LayoutInflater.from(activity);
}
public void setOnFeedItemClickListener(OnFeedItemClickListener onFeedItemClickListener) {
this.onFeedItemClickListener = onFeedItemClickListener;
}
#Override
public AddtoCartHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View v = LayoutInflater.from(parent.getContext()).inflate(
R.layout.feed_item, parent, false);
AddtoCartHolder viewHolder = new AddtoCartHolder(v);
setupClickableViews(v, viewHolder);
return viewHolder;
}
#Override
public void onBindViewHolder(final AddtoCartHolder holder, int position) {
CartItem item = (CartItem) filteredfeedItems.get(position);
holder.price.setText((String.valueOf(item.getProductName()) + ""));
holder.location.setText((String.valueOf(item.getQuantity())) + "");
}
private void setupClickableViews(final View view, final AddtoCartHolder cellFeedViewHolder) {
cellFeedViewHolder.plus.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if(onFeedItemClickListener !=null){
onFeedItemClickListener.onAddClick(v, cellFeedViewHolder.getAdapterPosition());
}
else{
Toast.makeText(activity, "Data is null", Toast.LENGTH_LONG).show();
}
}
});
}
public interface OnFeedItemClickListener {
void onAddClick(View v, int position);
}
#Override
public int getItemCount() {
return filteredfeedItems.size();
}
I am always getting null whenever clicking on button really down know why it is coming null...
Here is my fragment class which have implemented interface.
public class MyFragment extends Fragment implements FeedListAdapter.OnFeedItemClickListener{
// the method
#Override
public void onAddClick(View v, int position) {
// TODO Auto-generated method stub
Snackbar.make(clContent, "Product removed from cart!",
Snackbar.LENGTH_SHORT).show();
}
You must be instantiating a FeedListAdapter in your fragment correct buddy ???
Like using statement :
FeedListAdapter adapter = new FeedListAdapter(this.getActivity(),your_array_list)
After instantiating your adapter just call your adapter's setOnFeedItemClickListener with 'this' as argument :) That's all :)
adapter.setOnFeedItemClickListener(this)
Hope my answer helped you :) Happy coding buddy :)
By the looks of things your aren't setting your listener. Thus, onFeedItemClickListener is always null.
Also MyFragment isn't actually doing anything, you haven't inflated a view, overridden onCreateView(...), etc.
There's a few things that you could definitely change to improve your implementation. But to get your listener working:
Just get rid of MyFragment you don't appear to be using it properly.
Move your implements FeedListAdapter.OnFeedItemClickListener to your Activity. i.e. Make your Activity implement your OnFeedItemClickListener interface rather than MyFragment (which doesn't appear to be doing anything).
Make FeedListAdapter set the listener in its constructor:
public FeedListAdapter(Activity activity, ArrayList<CartItem> feedItems)
{
this.activity = activity;
// Assume we the activity implements OnFeedItemClickListener
setOnFeedItemClickListener((OnFeedItemClickListener)activity);
this.feedItems = feedItems;
this.filteredfeedItems = feedItems;
inflater = LayoutInflater.from(activity);
}
Please keep in mind that this is a pretty bad implementation and you can definitely improve on it but for the purpose of the question, it's sufficient.
I would like to set up a button that would allow me to move to another activity while I finished drawing my painting.
The question is I don't know how to set up this button in the view class because it doesn't listen to any listeners.
In your drawing view, define your interface
public class MyDrawingView extends View
{
protected MyPaintingListener m_paintingListener;
public interface MyPaintingListener
{
// you can define any parameter as per your requirement
public void paintingEnded();
}
public void onCreateView()
{
// Create your view
}
public void draw()
{
// Draw your painting
// then
if(m_paintingListener != null)
m_paintingListener.paintingEnded();
}
public void setListener(MyPaintingListener p_listener)
{
m_paintingListener = p_listener;
}
}
In your current Fragment or Activity:
public class MyActivity extends Activity
implements MyDrawingView.MyPaintingListener
{
protected MyDrawingView m_drawingView;
public void OnActivityCreated(Bundle savedInstanceState)
{
// In this method or another, create your drawingView
m_drawingView = new MyDrawingView();
m_drawingView.setListener(this);
m_drawingView.paint();
}
#Override
public void paintingEnded()
{
// Set up your button;
}
}
I have fragment with ListView, say MyListFragment, and custom CursorAdapter.
I'm setting onClickListener in this adapter for the button in the list row.
public class MyListAdapter extends CursorAdapter {
public interface AdapterInterface {
public void buttonPressed();
}
...
#Override
public void bindView(final View view, final Context context, final Cursor cursor) {
ViewHolder holder = (ViewHolder) view.getTag();
...
holder.button.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
// some action
// need to notify MyListFragment
}
});
}
}
public MyListFragment extends Fragment implements AdapterInterface {
#Override
public void buttonPressed() {
// some action
}
}
I need to notify fragment when the button is pressed. How to invoke this interface?
Help, please.
Make a new constructor and an instance variable:
AdapterInterface buttonListener;
public MyListAdapter (Context context, Cursor c, int flags, AdapterInterface buttonListener)
{
super(context,c,flags);
this.buttonListener = buttonListener;
}
When the Adapter is made, the instance variable will be given the proper reference to hold.
To call the Fragment from the click:
public void onClick(View v) {
buttonListener.buttonPressed();
}
When making the Adapter, you will have to also pass your Fragment off to the Adapter. For example
MyListAdapter adapter = new MyListAdapter (getActivity(), myCursor, myFlags, this);
since this will refer to your Fragment, which is now an AdapterInterface.
Keep in mind that on orientation of the Fragment changes, it will most likely be recreated. If your Adapter isn't recreated, it can potentially keep a reference to a nonexistent object, causing errors.
Using Eventbus:
Examples:
https://github.com/kaushikgopal/RxJava-Android-Samples/tree/master/app/src/main/java/com/morihacky/android/rxjava/rxbus
or
https://github.com/greenrobot/EventBus
Using Interfaces:
I understand the current answer but needed a more clear example. Here is an example of what I used with an Adapter(RecyclerView.Adapter) and a Fragment.
Create Callback Interface:
public interface AdapterCallback {
void onMethodCallback();
}
Passing in Callback/Fragment:
This will implement the interface that we have in our Adapter. In this example, it will be called when the user clicks on an item in the RecyclerView.
In your Fragment:
public class MyFragment extends Fragment implements AdapterCallback {
private MyAdapter mMyAdapter;
#Override
public void onMethodCallback() {
// do something
}
#Override
public void onCreate(final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
this.mMyAdapter = new MyAdapter(this); // this class implements callback
}
}
Use the Callback in your Adapter:
In the Fragment, we initiated our Adapter and passed this as an argument to the constructer. This will initiate our interface for our callback method. You can see that we use our callback method for user clicks.
public class MyAdapter extends RecyclerView.Adapter<MyAdapter.ViewHolder> {
private AdapterCallback mAdapterCallback;
public MyAdapter(AdapterCallback callback) {
this.mAdapterCallback = callback;
}
#Override
public void onBindViewHolder(final MyAdapter.ViewHolder viewHolder, final int i) {
// simple example, call interface here
// not complete
viewHolder.itemView.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
mAdapterCallback.onMethodCallback();
}
});
}
}
or Use the Fragment in your Adapter:
public class MyAdapter extends RecyclerView.Adapter<MyAdapter.ViewHolder> {
private AdapterCallback mAdapterCallback;
public MyAdapter(Fragment fragment) {
try {
this.mAdapterCallback = ((AdapterCallback) fragment);
} catch (ClassCastException e) {
throw new ClassCastException("Fragment must implement AdapterCallback.");
}
}
#Override
public void onBindViewHolder(final MyAdapter.ViewHolder viewHolder, final int i) {
// simple example, call interface here
// not complete
viewHolder.itemView.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
try {
mAdapterCallback.onMethodCallback();
} catch (ClassCastException exception) {
// do something
}
}
});
}
}
Follow the 2 steps below for receive callback from Adapter in Fragment (or Activity)
First: In your Adapter
public class ListAdapter extends RecyclerView.Adapter < RecyclerListAdapter.ItemViewHolder > {
...
private ListAdapterListener mListener;
public interface ListAdapterListener { // create an interface
void onClickAtOKButton(int position); // create callback function
}
public RecyclerListAdapter(Context mContext, ArrayList < Items > listItems, ListAdapterListener mListener) { // add the interface to your adapter constructor
...
this.mListener = mListener; // receive mListener from Fragment (or Activity)
}
...
public void onBindViewHolder(final ItemViewHolder holder, final int position) {
holder.btnOK.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
// use callback function in the place you want
mListener.onClickAtOKButton(position);
}
});
...
}
...
}
Second: In your Fragment (or Activity), there are 2 ways for implement callback method
Way 1
public MyListFragment extends Fragment {
...
public void onViewCreated(View view, #Nullable Bundle savedInstanceState) {
...
ListAdapter adapter = new ListAdapter(getActivity(), listItems, new ListAdapter.ListAdapterListener() {
#Override
public void onClickAtOKButton(int position) {
Toast.makeText(getActivity(), "click ok button at" + position, Toast.LENGTH_SHORT).show();
}
});
...
}
}
Way 2
public MyListFragment extends Fragment implements ListAdapter.ListAdapterListener {
...
public void onViewCreated(View view, #Nullable Bundle savedInstanceState) {
ListAdapter adapter = new ListAdapter (getActivity(), listItems, this);
...
}
#Override
public void onClickAtOKButton(int position) {
Toast.makeText(getActivity(), "click ok button at" + position, Toast.LENGTH_SHORT).show();
}
}
This is very similar to the way an activity and a fragment should communicate. In the constructor of your adapter, pass a reference of your fragment, cast it to your interface and just call yourReference.buttonPressed() on your onClick method.
a solution for NPE is first to make conctractor in your Fragment like that
public MyFragment MyFragment(){
return this;
}
then initialize your listener is adapter like that
Lisener lisener = new MyFragment();
Make a constructor like that:
public MyAdapter(Activity activity,AlertMessageBoxOk alertMessageBoxOk) {
this.mActivity = activity;
mAlertMessageBoxOk = alertMessageBoxOk;
}
call the interface from adapter use any event
mAlertMessageBoxOk.onOkClick(5);
after that implement AlertMessageBoxOk interface to your fragment like this,
class MyFragment extends Fragment implements AlertMessageBoxOk {
#Override
public void onOkClick(int resultCode) {
if(resultCode==5){
enter code here
}
}
}
Is there a standard set of Listener/Observer/Observable classes in Android for managing application events in Android?
I'm not talking about UI or other Android API events, but rather custom app events like GameOverEvent, LevelClearedEvent, etc.
Is there a preferred interface to implement/extend so that I can implement things like:
public void addGameOverListener(GameOverListener listener)
It's easy,, you just need to create your own EventListener
public interface onGameFinishedListener {
public void onGameFinished(GameView gameView);
}
and some class which has onGameFinished() method
public abstract class GameView extends SurfaceView implements SurfaceHolder.Callback{
List<onGameFinishedListener> listeners;
public GameThread gameThread;
protected int width;
protected int height;
public GameView(Context context) {
super(context);
width = 320;
height = 480;
listeners = new ArrayList<onGameFinishedListener>();
}
public abstract void init();
public void registerGameFinishedListener(onGameFinishedListener listener) {
listeners.add(listener);
}
protected void GameFinished(GameView gameView) {
for (onGameFinishedListener listener : listeners) {
synchronized(gameThread.getSurfaceHolder()) {
listener.onGameFinished(gameView);
}
}
}
}
and then you implement the onGameFinishedListener in your activity or view which you want to do operation when the game finish,
public class RocketActivity extends GameActivity implements onGameFinishedListener {
private final int MENU = 0;
private final int END = 1;
private final int CONFIRMATION = 2;
private RelativeLayout layout;
private RocketView rocketView;
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
layout = new RelativeLayout(this);
rocketView = new RocketView(this);
rocketView.registerGameFinishedListener(this);
rocketView.init();
layout.addView(rocketView);
setContentView(layout);
}
#Override
public void onGameFinished(GameView gameView) {
runOnUiThread(new Runnable() {
#Override
public void run() {
showDialog(END);
}
});
}
}
there. no need to rely on Android for EventListener. :)
Have you tried EventBus by GreenRobot?
It is basically a pretty standard implementation of an eventBus for handling application wide events.
It provides inter-thread communication which is quite neat.
Pretty similar to what you get for GWT