I am working on an android app and I have a custom GUI component which extends a TextView.
I want to have my custom control do a task when clicked from my custom control class and my overridden onclick method.
For example my class that extends the TextView implements the OnClick listener and writes a log to the log cat.
Then in my activity, I set an onclick listener to my custom control, and this shows a toast notification.
What I want to happen, is when my custom control is clicked, my activities overridden onclick shows the toast and the custom control class on click method also is run to show the log. But I can only seem to get one working or the other, for example, if I don't run myCustom.setOnClickListener(myListener) then the classes onclick is used and does the log, if I set the onClick listener then I only get the toast not the log.
Below is my custom control class
public class NavTextView extends TextView implements View.OnClickListener
{
public NavTextView(Context context) {
super(context);
setOnClickListener(this);
}
public NavTextView(Context context, AttributeSet attrs) {
super(context, attrs);
setOnClickListener(this);
}
public NavTextView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
setOnClickListener(this);
}
public NavTextView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
setOnClickListener(this);
}
#Override
public void onClick(View v) {
Log.d("NavTextView", "This has been clicked");
}
}
Below is my activities onCreate method
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
navTextView = (NavTextView)findViewById(R.id.navTextView);
navTextView.setOnClickListener(mClickListener);
}
Hope this makes sense
A View can only have one OnClickListener. In your NavTextView you are setting it there. If you later call setOnClickListener again, you are replacing the previous listener.
What you can do is override setOnClickListener in your custom View, then wrap the OnClickListener and call both.
public class MyTextView extends TextView implements View.OnClickListener
{
OnClickListener _wrappedOnClickListener;
public MyTextView(Context context) {
super(context);
super.setOnClickListener(this);
}
#Override
public void onClick(View view) {
Log.d("NavTextView", "This has been clicked");
if (_wrappedOnClickListener != null)
_wrappedOnClickListener.onClick(view);
}
#Override
public void setOnClickListener(OnClickListener l) {
_wrappedOnClickListener = l;
}
}
Related
I'm new to Android. I wrote a custom view, but I just don't know where to call the bind and unbind method. I have searched for this documentation. And it seemingly suggests to use bind in the onFinishInflate() callback. But I bind the view in its constructor function and there is no crash any way. Is it correct? And how about the unbind? I find this question, it suggests to use unbind in the onDetachedFromWindow() callback. Is it required or necessary?
public class BloodIndicatorView extends FrameLayout {
#Bind(R.id.ll_record_bloodpress)
LinearLayout llRecordBloodpress;
#Bind(R.id.ll_record_bloodsugar)
LinearLayout llRecordBloodsugar;
private Context mContext;
public BloodIndicatorView(Context context) {
this(context, null);
}
public BloodIndicatorView(Context context, AttributeSet attrs) {
this(context, attrs, -1);
}
public BloodIndicatorView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
mContext = context;
init();
}
private void init() {
LayoutInflater.from(mContext).inflate(R.layout.health_blood_indicator, this);
ButterKnife.bind(this);
}
public void update() {
}
#OnClick(R.id.ll_record_bloodpress)
public void recordBloodpress() {
Intent intent = BloodPressActivity.getIntent2Act(mContext);
mContext.startActivity(intent);
}
#OnClick(R.id.ll_record_bloodsugar)
public void recordBloodsugar() {
Intent intent = BloodSugarActivity.getIntent2Act(mContext);
mContext.startActivity(intent);
}
}
You can annotate the view inside the class which you are doing correctly and since there is no error that means ButterKnife.bind(this); is happening correctly. And its not necessary to unbind this should work completely fine.
I'm making a custom button which is designed to handle onClick events internally. Here's a simplified version of my implementation:
public class CustomButton extends Button implements View.OnClickListener {
public CustomButton(Context context) {
super(context);
setOnClickListener(this);
}
#Override
public void onClick(View v) {
setText("Clicked!");
}
}
No surprises in the above code! The problem is that the activity/fragment/whatever containing the button could call setOnClickListener(View) which would prevent the onClick(View) method inside the button from receiving the onClick event, thus changing the function of my button in an undesirable way. I though about overriding setOnClickListener(View) to throw an exception if the argument is not this, but I don't want to prevent the button from having external listeners, I just want to add an internal feature.
Which method of Button if any can I override to do extra stuff when the button is clicked without taking away the ability of other classes to listen to onClick events? Basically, which method in Button dispatches OnClick events?
I thought of a solution. I made CustomButton as follows:
public class CustomButton extends Button implements View.OnClickListener {
private OnClickListener externalOnClickListener;
public CustomButton(Context context) {
super(context);
super.setOnClickListener(this);
}
public CustomButton(Context context, AttributeSet attrs) {
super(context, attrs);
super.setOnClickListener(this);
}
public CustomButton(Context context, AttributeSet attrs, int defStyleAttrs) {
super(context, attrs, defStyleAttrs);
super.setOnClickListener(this);
}
#Override
public void onClick(View v) {
// Do all of my stuff
setText("Clicked!");
setBackground(Color.GREEN);
// Pass onClick event to external listener
if (externalOnClickListener != null) {
externalOnClickListener.onClick(v);
}
}
#Override
public void setOnClickListener(OnClickListener l) {
externalOnClickListener = l;
}
}
Basically setOnClickListener() is overriden so that it just stores the passed listener as an instance variable. At construction, the button sets itself as its own on click listener, so that it can receive the on click events, do something, then pass the event on to the stored listener.
I started to use RoboGuice within my project. I can easily inject views inside fragments and activites but i have some trouble with cusom views.
I got null ptr exception every time.
According to RoboGuice's example i did the same with my custom class:
TestActivity
#ContentView(R.layout.test_layout)
public class TestActivity extends RoboActivity {
#InjectView(R.id.testView_1) TestView testView;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
}
TestView
public class TestView extends LinearLayout {
#InjectView(R.id.log_in_tab) View logInTab;
public TestView(Context context) {
super(context);
initView();
}
public TestView(Context context, AttributeSet attrs) {
super(context, attrs);
initView();
}
public TestView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initView();
}
#Override
public void onFinishInflate() {
super.onFinishInflate();
if (logInTab == null)
Toast.makeText(getContext(), "Still NULL", Toast.LENGTH_LONG).show();
else
Toast.makeText(getContext(), "Ok", Toast.LENGTH_LONG).show();
}
public void initView() {
inflate(getContext(), R.layout.login_view, this);
RoboGuice.injectMembers(getContext(), this);
}
}
Login view's xml is in pastebin here.
Test layout
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<view
android:layout_width="wrap_content"
android:layout_height="wrap_content"
class="hu.illion.kwindoo.view.test.TestView"
android:id="#+id/testView_1"/>
</LinearLayout>
Toast always says that logInTab is null.
Please help if you can.
I don't know why there is no code examples of that but when i have to inject custom views i use injectViewMembers.
Hope this work for you:
public void initView() {
inflate(getContext(), R.layout.login_view, this);
RoboGuice.injectMembers(getContext(), this);
RoboGuice.getInjector(getContext()).injectViewMembers(this);
}
In addition to the previous answer, you should use the following method to actually start using the injected views:
#Override
protected void onFinishInflate() {
super.onFinishInflate();
someTextView.setText("Some text");
}
i would like to create a class, which extends Button and implement a method, which is alwasy called, when the Button is clicked. But i still want it's OnClickListener to be called.
My Idea is to save the OnClickListener into a private member when the constructor or setOnClickListener is called and then set the OnClickListener to my own OnClickListener. This one would then call my method and the saved OnClickListener.
But i don't see how i can get the OnClickListenr, i only see, how to set it.
Is there a way to acces it?
Or do you have a better idea? (it doesn't matter wheter my method is called before or after the OnClickListener)
I guess you could do this:
public class OnceClickedTwiceRunButton extends Button{
public OnceClickedTwiceRunButton(Context context) {
super(context);
}
public OnceClickedTwiceRunButton(Context context, AttributeSet attrs) {
super(context, attrs);
}
public OnceClickedTwiceRunButton(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
private OnClickListener extraClickMethod;
#Override
public void setOnClickListener(OnClickListener newListener)
{
super.setOnClickListener(new OnClickListener(){
#Override
public void onClick(View v) {
DefaultClickMethod(v);
if(extraClickMethod != null)
{
extraClickMethod.onClick(v);
}
}
});
extraClickMethod = newListener;
}
private void DefaultClickMethod(View v)
{
//TODO
}
}
I have a custom view MyView inflated from my_view.xml (with a corresponding Java class MyView.java).
I override MyView(Context context, AttributeSet attrs) to connect subviews up to members of the class.
Now one of these subviews is a Button, and I'd like for my view to listen for a click on its button before passing this event on to a delegate. However if I declare
this.myButton.setOnClickListener(this);
in the constructor MyView(Context context, AttributeSet attrs) (where this is an instance of MyView) I get a NullPointerException.
Where is an appropriate place in MyClass.java to call this.myButton.setOnClickListener(this);?
%%
Edit. Some code:
public MyView(Context ctx, AttributeSet attrs)
{
super(context, attrs);
this.myButton = (Button) this.findViewById(R.id.my_button);
this.myButton.setOnClickListener(this); // Raises null pointer;'id' is correct.
}
Instead of trying to do the setOnClickListener(this) call in the constructor, do it after the button has been fully initialized. Try moving setOnClickListener(this) so that it's called (indirectly) from the parent activity's onResume method like this:
public class MainMenuActivity extends Activity {
#Override
public void onResume() {
super.onResume();
new MyView(this, attrs).onResume();
}
...
}
public class MyView {
public void onResume() {
myButton.setOnClickListener(this);
}
...
}
I use Roboguice all the time for this sort of thing, and highly recommend it. My button handler code looks like this:
class ButtonHandler implements OnClickListener {
#InjectView(R.id.MainMenuButton)
private Button button;
public void onResumeEvent( #Observes OnResumeEvent onResume ) {
button.setOnClickListener(this);
}
#Override
public void onClick(View v) {
doSomethingUseful();
}
}
The problem is I've missed off an inflate in the constructor:
LayoutInflater i = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
i.inflate(R.layout.my_view, this);
This is confusing for me as I'd thought the constructor MyView(Context ctx, AttributeSet attrs) would be called in inflating the view, not the other way around.