I have searched but could not find answer of my question.
This is what I have:
private class BoxView extends View {
private String caption;
private OnClickListener bvClickListener = null
public BoxView(Context context) {
super(context);
this.bvClickListener = new this.OnClickListener(){
public void onClick (View v){
/*v.setCaption("X"); view don't have this method */
}}
}
public void setCaption(String s){
this.caption=s;
invalidate();
}
}
This is what I want to have:
private class BoxView extends View {
private String caption;
private OnClickListener bvClickListener = null
public BoxView(Context context) {
super(context);
this.bvClickListener = new this.OnClickListener(){
public void onClick (BoxView bv){
bv.setCaption("X");
}}
}
public void setCaption(String s){
this.caption=s;
invalidate();
}
}
I may need custom methods for my custom views. And I want to be able to pass my custom view instead of view version of it when onclick is triggered so I can access to it directly.
Updated
And I want to have access to real object not a converted one. So I want to avoid this:
public void onClick (View v){
((BoxView)v).setCaption("X");
}
Call setCaption method as in onClick :
public void onClick (View v){
((BoxView)v).setCaption("X");
}
Try this
class Main extents Activity
{
BoxView boxView;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// if view is used using layout then
boxView = (BoxView)findViewByID(id);
//else if directly used
boxView = new BoxView(this);
box.setOnClickListener(new onClickListener()
{
#Override
public void onClick(View view) {
boxView.setCaption("X");
boxView.invalidate();
}
});
}
}
Related
I'm currently having trouble setting up my custom listener. I just want to pass a string from my dialog to my fragment (where I set up the dialog). I was trying to follow this tutorial: https://www.youtube.com/watch?v=ARezg1D9Zd0.
At minute 10:38, he sets up the listener.
This only problem is that in this, he uses DialogFragment, but I'm extending dialog and I don't know how to attach the context to the listener.
I've tried to set it up in onAttachedToWindow() and in the dialog constructor but it crashes.
What should I actually do?
I'd also appreciate it if someone could explain what the difference is between:
onAttachedToWindow() vs. onAttach(Context context).
Thanks!
MY CUSTOM DIALOG BOX:
public class NewListDialog extends Dialog implements View.OnClickListener {
private Activity c;
private TextInputLayout textInputLayout;
private TextInputEditText editText;
private LinearLayout dialog_root_view;
private Animation fade_out;
private String list_name;
private NewListDialogListener listener;
NewListDialog(Activity a) {
super(a);
this.c = a;
//ANOTHER ATTEMPT TO ATTACH CONTEXT TO LISTENER
//listener = (NewListDialogListener) a.getApplicationContext();
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.new_list_dialog);
MaterialButton cancel = findViewById(R.id.dialog_new_list_cancel_button);
MaterialButton create = findViewById(R.id.dialog_new_list_create_button);
textInputLayout = findViewById(R.id.dialog_text_input_layout);
editText = findViewById(R.id.dialog_edit_text);
dialog_root_view = findViewById(R.id.dialog_root);
fade_out = AnimationUtils.loadAnimation(c, R.anim.fade_out_dialog);
editText.setOnKeyListener(new View.OnKeyListener() {
#Override
public boolean onKey(View view, int i, KeyEvent keyEvent) {
if (isTextValid(editText.getText())) {
textInputLayout.setError(null);
return true;
}
return false;
}
});
cancel.setOnClickListener(this);
create.setOnClickListener(this);
}
#Override
public void onClick(View view) {
switch (view.getId()) {
//Cancel Button
case R.id.dialog_new_list_cancel_button:
dialog_root_view.startAnimation(fade_out);
final Handler handler = new Handler();
handler.postDelayed(new Runnable() {
#Override
public void run() {
dismiss();
}
}, 200);
break;
//Create Button
case R.id.dialog_new_list_create_button:
if (!isTextValid(editText.getText())) {
textInputLayout.setError(c.getString(R.string.dialog_error));
} else {
textInputLayout.setError(null);
//record input string
list_name = editText.getText().toString();
//send information to parent activity
//What to put here?
listener.createListName(list_name);
dismiss();
}
break;
default:
break;
}
}
private boolean isTextValid(#Nullable Editable text) {
return text != null && text.length() > 0;
}
//ATTEMPT TO ATTACH CONTEXT TO LISTENER
#Override
public void onAttachedToWindow() {
super.onAttachedToWindow();
try {
listener = (NewListDialogListener) c.getBaseContext();
} catch (ClassCastException e) {
throw new ClassCastException(c.getBaseContext().toString() + "must implement ExampleDialogListener");
}
}
public interface NewListDialogListener {
void createListName(String listname);
}
}
In case you define a custom dialog then you can declare a method to allow other components call it or listen events on this dialog. Add this method to you custom dialog.
public void setNewListDialogListener(NewListDialogListener listener){
this.listener = listener;
}
NewListDialog.java
public class NewListDialog extends Dialog implements View.OnClickListener {
private Activity c;
private TextInputLayout textInputLayout;
private TextInputEditText editText;
private LinearLayout dialog_root_view;
private Animation fade_out;
private String list_name;
private NewListDialogListener listener;
NewListDialog(Activity a) {
super(a);
this.c = a;
}
public void setNewListDialogListener(NewListDialogListener listener) {
this.listener = listener;
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.new_list_dialog);
MaterialButton cancel = findViewById(R.id.dialog_new_list_cancel_button);
MaterialButton create = findViewById(R.id.dialog_new_list_create_button);
textInputLayout = findViewById(R.id.dialog_text_input_layout);
editText = findViewById(R.id.dialog_edit_text);
dialog_root_view = findViewById(R.id.dialog_root);
fade_out = AnimationUtils.loadAnimation(c, R.anim.fade_out_dialog);
editText.setOnKeyListener(new View.OnKeyListener() {
#Override
public boolean onKey(View view, int i, KeyEvent keyEvent) {
if (isTextValid(editText.getText())) {
textInputLayout.setError(null);
return true;
}
return false;
}
});
cancel.setOnClickListener(this);
create.setOnClickListener(this);
}
#Override
public void onClick(View view) {
switch (view.getId()) {
//Cancel Button
case R.id.dialog_new_list_cancel_button:
dialog_root_view.startAnimation(fade_out);
final Handler handler = new Handler();
handler.postDelayed(new Runnable() {
#Override
public void run() {
dismiss();
}
}, 200);
break;
//Create Button
case R.id.dialog_new_list_create_button:
if (!isTextValid(editText.getText())) {
textInputLayout.setError(c.getString(R.string.dialog_error));
} else {
textInputLayout.setError(null);
//record input string
list_name = editText.getText().toString();
//send information to parent activity
//What to put here?
if (listener != null) {
listener.createListName(list_name);
}
dismiss();
}
break;
default:
break;
}
}
private boolean isTextValid(#Nullable Editable text) {
return text != null && text.length() > 0;
}
public interface NewListDialogListener {
void createListName(String listname);
}
}
In other components such as an activity which must implements NewListDialogListener.
NewListDialog dialog = new NewListDialog(this);
dialog.setNewListDialogListener(this);
If you don't want the activity implements NewListDialogListener then you can pass a listener instead.
NewListDialog dialog = new NewListDialog(this);
dialog.setNewListDialogListener(new NewListDialog.NewListDialogListener() {
#Override
public void createListName(String listname) {
// TODO: Your code here
}
});
In android Fragments and Activity has lifecycles. Fragments are hosted inside Activity and get the context of host activity via onattach method.
On the other hand Dialog is extended from Object (God class) without any lifecycle and should be treaded as an object.
If your activity is implementing NewListDialogListener then you can do
listener = (NewListDialogListener) a;
onAttachedToWindow : mean the dialog will be drawn on screen soon
and
getApplicationContext() will give you the context object of the application (one per app) which is surely not related with your listener and hence won't work
Reference :
Android DialogFragment vs Dialog
Difference between getContext() , getApplicationContext() , getBaseContext() and “this”
You can use RxAndroid instead of using listener, in this situation I use RxAndroid to get data from dialogs to activities or fragments.
Just need to create a PublishSubject and get the observed data. on activity or fragment :
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
PublishSubject<String > objectPublishSubject = PublishSubject.create();
objectPublishSubject.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.newThread())
.subscribe(this::onNext);
CustomDialog customDialog = new CustomDialog(this, objectPublishSubject);
customDialog.show();
}
private void onNext(String data) {
Log.i("DIALOG_DATA", data);
}
and you can create dialog like this :
public class CustomDialog extends Dialog implements View.OnClickListener {
private PublishSubject<String> subject;
public CustomDialog(#NonNull Context context, PublishSubject<String> subject) {
super(context);
this.subject = subject;
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.custom_dialog);
findViewById(R.id.button).setOnClickListener(this);
}
#Override
public void onClick(View v) {
subject.onNext("Data");
dismiss();
}
I wrote a compound component and was adding a custom listener to react.
Inside the class for the compound component which uses an xml file.
public class VerticalCounterBlock extends LinearLayout {
public interface VerticalCounterBlockListener {
public void onCountChanged(int newCount);
}
private VerticalCounterBlockListener mVerticalCounterBlockListener = null;
public void setVerticalCounterBlockListener(VerticalCounterBlockListener listener){
mVerticalCounterBlockListener = listener;
}
// ... Other functions
}
I got my interface, I got the listener and I got the setter and I engage the listener like this in the button I have in the compound component. I can see that toast that is showing there when I test
addBtn = (Button)findViewById(R.id.btn_addcount);
addBtn.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View view) {
count++;
counttv.setText(String.format("%1$d", count));
Toast.makeText(getContext(), "VCB", Toast.LENGTH_SHORT).show();
if(mVerticalCounterBlockListener != null) {
mVerticalCounterBlockListener.onCountChanged(count);
}
}
});
In my main activity
m20_vcb = (VerticalCounterBlock) findViewById(R.id.vcb_m20);
m20_vcb.setVerticalCounterBlockListener(new VerticalCounterBlock.VerticalCounterBlockListener() {
#Override
public void onCountChanged(int newCount) {
increasePreachCountTotal();
Toast.makeText(CounterActivity.this, String.format("%1$d", newCount), Toast.LENGTH_SHORT).show();
}
});
I do not see that toast nor does it engage the function call. What am I missing?
I can suggest you several improvement scope here mainly restructuring the current format.
Lets not keep the interface as a inner class. So here's your VerticalCounterBlockListener.java
public interface VerticalCounterBlockListener {
public void onCountChanged(int newCount);
}
Now implement this interface in your MainActivity
public class MainActivity implements VerticalCounterBlockListener {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
m20_vcb = (VerticalCounterBlock) findViewById(R.id.vcb_m20);
m20_vcb.setVerticalCounterBlockListener(this);
}
// ... Other methods
// Override the onCountChanged function.
#Override
public void onCountChanged(int newCount) {
increasePreachCountTotal();
Toast.makeText(CounterActivity.this, String.format("%1$d", newCount), Toast.LENGTH_SHORT).show();
}
}
You might consider removing the Toast from the addBtn click listener which might create exception.
addBtn = (Button)findViewById(R.id.btn_addcount);
addBtn.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View view) {
count++;
counttv.setText(String.format("%1$d", count));
if(mVerticalCounterBlockListener != null) {
mVerticalCounterBlockListener.onCountChanged(count);
}
}
});
This was good there was something wrong with my system. i uninstaklled app and restarted computer and it worked as expected.
I want to add a custom view (that contains a layout with button and text view) to every Activity I need my custom view name is NetworkErrorView and I have another class that help me observe network status change!
I want to show my custom view on top of other view in every activity that I bulid my NetworkErrorView like this
private NetworkErrorView networkErrorView=new NetworkErrorView(this).build();
And when network stats is change I want to change visibility from Gone to Visible(onChange is called when my network status changed):
#Override
public void onChange(boolean isConnected) {
networkErrorView.networkErorrDialog(isConnected);
}
My onChange() (method work correctly but i cant see my custom view when I change visibility! Can anyone help me??
NetworkErrorView:
public class NetworkErrorView {
private ViewGroup mRootView;
private Activity mActivity;
private LinearLayoutManager mLayoutManager;
private View view;
private Animation translationIn;
private Button btnNetwork;
private LinearLayout networkContainer;
private boolean networkStatus;
public NetworkErrorView(#NonNull Activity activity) {
this.mRootView = (ViewGroup) activity.findViewById(android.R.id.content);
this.mActivity = activity;
this.mLayoutManager = new LinearLayoutManager(mActivity);
}
public NetworkErrorView build(){
view= Assist.inflater.inflate(R.layout.dialog_network_error,mRootView,false);
//View.inflate(mActivity, R.layout.dialog_network_error, mRootView);
btnNetwork=(Button) view.findViewById(R.id.btn_error_network);
btnNetwork.setOnClickListener(onClickListener);
networkContainer=(LinearLayout) view.findViewById(R.id.layout_error_networkcontainer);
translationIn= AnimationUtils.loadAnimation(mActivity,R.anim.anim_wifi_container_in);
mRootView.addView(view, 1);
return this;
}
public void networkErorrDialog(boolean isConnected){
networkStatus=isConnected;
if(isConnected){
view.setVisibility(View.GONE);
}else {
view.setVisibility(View.VISIBLE);
view.bringToFront();
translationIn= AnimationUtils.loadAnimation(mActivity, R.anim.anim_wifi_container_in);
networkContainer.setAnimation(translationIn);
}
}
View.OnClickListener onClickListener=new View.OnClickListener() {
#Override
public void onClick(View v) {
if(!networkStatus){
mActivity.startActivity(new Intent(Settings.ACTION_WIFI_SETTINGS));
}
}
};
}
mActivity:
public class mActivity extends AppCompatActivity implements NetworkObserver {
private NetworkErrorView networkErrorView;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_profile);
FontManager.instance().setTypeface(getWindow().getDecorView());
addView();
NetworkManager.init(this);
//...
}
private void addView(){
//...
networkErrorView=new NetworkErrorView(this).build();
}
#Override
public void onChange(boolean isConnected) {
networkErrorView.networkErorrDialog(isConnected);
}
}
I think the view is being added, but because you are adding the view in the 1th position it is not being shown so try something like this
public NetworkErrorView build(){
view= Assist.inflater.inflate(R.layout.dialog_network_error,mRootView,false);
//View.inflate(mActivity, R.layout.dialog_network_error, mRootView);
btnNetwork=(Button) view.findViewById(R.id.btn_error_network);
btnNetwork.setOnClickListener(onClickListener);
networkContainer=(LinearLayout) view.findViewById(R.id.layout_error_networkcontainer);
translationIn= AnimationUtils.loadAnimation(mActivity,R.anim.anim_wifi_container_in);
mRootView.addView(view, 0); //Change this form 1 to 0
return this;
}
In android, I have a custom layout I built:
public class ButtonMatch extends RelativeLayout
{
private final TextView text_round, text_match, text_player1, text_player2;
public ButtonMatch(final Context context) {
super(context);
LayoutInflater.from(context).inflate(R.layout.button_match, this, true);
text_round = (TextView) findViewById(R.id.text_round);
text_match = (TextView) findViewById(R.id.text_match);
text_player1 = (TextView) findViewById(R.id.text_player1);
text_player2 = (TextView) findViewById(R.id.text_player2);
}
public void setRound(String text) {
text_round.setText(text);
}
public void setMatch(String text) {
text_match.setText(text);
}
public void setPlayer1(String text) {
text_player1.setText(text);
}
public void setPlayer2(String text) {
text_player2.setText(text);
}
public String getPlayer1() {
return text_player1.getText();
}
public String getPlayer2() {
return text_player2.getText();
}
}
Then I am adding this layout in code with the following:
ButtonMatch button = new ButtonMatch(this);
button.setLayoutParams(layout);
button.setTag(match.get("id"));
button.setMatch(match.get("identifier").toString());
button.setPlayer1(players.get(match.get("player1_id").toString()));
button.setPlayer2(players.get(match.get("player2_id").toString()));
button.setOnClickListener(new View.OnClickListener()
{
public void onClick(View view)
{
Intent intent = new Intent(view.getContext(), ChallongeMatch.class);
intent.putExtra(MainActivity.EXTRA_API_KEY, API_KEY);
intent.putExtra(MainActivity.EXTRA_SUBDOMAIN, SUBDOMAIN);
intent.putExtra(MainActivity.EXTRA_EVENT_ID, EVENT_ID);
intent.putExtra(MainActivity.EXTRA_MATCH_ID, view.getTag().toString());
intent.putExtra(MainActivity.EXTRA_PLAYER1, view.getPlayer1());
intent.putExtra(MainActivity.EXTRA_PLAYER2, view.getPlayer2());
}
});
What I am missing though, is in the Intent with the onClickListener, I am trying to get the contents of the text_player1 and text_player2 as follows:
intent.putExtra(MainActivity.EXTRA_PLAYER1, view.getPlayer1());
intent.putExtra(MainActivity.EXTRA_PLAYER2, view.getPlayer2());
The problem is those two functions don't work getPlayer1() and getPlayer2, because they dont exist in the view...
How do I get these two functions to work. I don't know a lot about Android/Java yet, so please be explain as much as you can.
You forgot casting view to ButtonMatch.
This is safe, because you set the click listener on the button object.
Change your code to
((ButtonMatch) view).getPlayer1()
to access the method on your object.
I want to create by code an array of objects that are subclasses of Button.
public class MyButton extends Button {
private Context ctx;
private int status;
public MyButton(Context context) {
super(context);
ctx = context;
status = 0;
}
private click() {
status = 1;
// OTHER CODE THAT NEEDS TO STAY HERE
}
}
In the main activity I do this:
public class myActivity extends Activity {
private MyButton[] myButtons = new MyButton[100];
#Override
public onCreate(Bundle si) {
super.onCreate(si);
createButtons();
}
private void createButtons() {
for (int w=0; w<100; w++) {
myButtons[w] = new MyButton(myActivity.this);
myButtons[w].setOnClickListener(new View.onClickListener() {
public void onClick(View v) {
// ... (A)
}
});
}
}
}
Now I want the click() method inside MyButton to be run each time the button is clicked.
Seems obvious but it is not at my eyes.
If I make the click() method public and run it directly from (A), I get an error because myButtons[w].click() is not static and cannot be run from there.
In the meantime, I an not able to understand where to put the code in the MyButton class to intercept a click and run click() from there. Should I override onClick? Or should I override onClickListener? Or what else should I do?
How can I run click() whenever one of myButtons[] object is clicked?
Thanks for the help.
You can cast View v you got in listener to MyButton and call click on it:
private void createButtons() {
View.OnClickListener listener = new View.onClickListener() {
public void onClick(View v) {
((MyButton) v).click();
}
};
for (int w=0; w<100; w++) {
myButtons[w] = new MyButton(myActivity.this);
myButtons[w].setOnClickListener(listener);
}
}
you can add:
View.onClickListener onclick = new View.onClickListener() {
public void onClick(View v) {
((MyButton)v).click();
//since v should be instance of MyButton
}
};
to your Activity
then use:
myButtons[w].setOnClickListener(onclick);
//one instance of onclick is enough, there is no need to create it for every button
in createButtons()
but ... why, oh why array of buttons we have ListView in android ...