When I build a View in Android dynamically I have to add it to a "parent" ViewGroup by calling
myLinearLayout.addView(myView);
I know that I can supervise the ViewGroup for any children to be added via the excellent onHierarchyChangeListener, but in my case I need the feedback in the View itself. Therefore my question is:
Is there something like a View.onAddedToParent() callback or a listener that I can build on?
To make things very clear: I want the view to handle everything on its own, I am aware of the fact that I could catch the event in the "parent" and then notify the view about things, but this is not desired here. I can only alter the view
Edit: I just found onAttachStateChangeListener and it would seem to work for most situations, but I'm wondering if this is really the correct solution. I'm thinking a View might just as well be passed on from one ViewGroup to another without being detached from the window. So I would not receive an event even though I want to. Could you please elaborate on this if you have insight?
Thanks in advance
You can create custom view and do your stuff in its onAttachedToWindow
public class CustomView extends View {
public CustomView(Context context) {
super(context);
}
#Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
Log.d("CustomView", "onAttachedToWindow called for " + getId());
Toast.makeText(getContext(), "added", 1000).show();
}
}
If you want to ensure that your customview added to correct viewgroup which you want
#Override
protected void onAttachedToWindow() {
// TODO Auto-generated method stub
super.onAttachedToWindow();
if(((View)getParent()).getId()== R.id.relativelayout2)
{
Log.d("CustomView","onAttachedToWindow called for " + getId());
Toast.makeText(context, "added", 1000).show();
}
}
According to the Android source code, a view can't be moved to another layout unless removeView() is called first on its parent, and if you look at the code of removeView(), it calls removeViewInternal(), which in turn calls an overload of removeViewInternal(), which, on this line calls view.dispatchDetachedFromWindow(), which, based on the Android source code on this line calls onDetachedFromWindow(). Then the view gets added using addView(), which calls onAttachedToWindow() in the same way.
In my opion you want like this;
CreateViews;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
final LinearLayout layout = new LinearLayout(this);
layout.setOrientation(LinearLayout.VERTICAL);
layout.setOnHierarchyChangeListener(new OnHierarchyChangeListener() {
#Override
public void onChildViewRemoved(View parent, View child) {
Log.e("View","removed");
if(child instanceof CustomButton){
CustomButton button = (CustomButton)child;
button.addListener();
}
}
#Override
public void onChildViewAdded(View parent, View child) {
Log.e("View","added");
if(child instanceof CustomButton){
CustomButton button = (CustomButton)child;
button.addListener();
}
}
});
for(int i = 0; i < 10; ++i){
CustomButton view = new CustomButton(this);
view.setText("Button "+i);
layout.addView(view, new LinearLayout.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT));
view.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
layout.removeViewAt(layout.getChildCount()-1);
}
});
}
setContentView(layout);
}
Listener;
public interface OnAddedListener {
public void addListener();
}
CustomButton class;
public class CustomButton extends Button implements OnAddedListener{
public CustomButton(Context context) {
super(context);
// TODO Auto-generated constructor stub
}
public CustomButton(Context context, AttributeSet attrs) {
super(context, attrs);
// TODO Auto-generated constructor stub
}
public CustomButton(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
// TODO Auto-generated constructor stub
}
#Override
public void addListener() {
Log.e("","In button add listener");
}
}
Related
I found this link that states how I could make the glow effect on my ImageButton. So whenever the user press the ImageButton the onTouchEvent() function is called and there I call the setImageBitmap() function. The problem is this function (setImageBitmap()) seems to have no effect at all.
What works:
1) So far I extended the ImageButton as below
// Pen.java
public class Pen extends ImageButton {
public Pen(Context context, AttributeSet attrs) {
super(context, attrs);
}
#Override
public boolean onTouchEvent(MotionEvent event) {
System.out.println("Now I am setting Pen");
return true;
}
}
Then inside Activity I create Pen (custom ImageButton) instance, define setGlow() and call setImageBitmap() from instance of Pen.
//MainActivity.java
public class MainActivity extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Pen myImgbutton;
myImgbutton = (Pen) findViewById(R.id.pen);
myImgbutton.setImageBitmap(setGlow(R.drawable.pen));
}
}
public Bitmap setGlow(int resourceId) {
......
}
What doesn't work:
1) All same as above ( extending a ImageButton in android ), but this time I am calling the
setImageBitmap function from inside onTouchEvent(). For this case I have define setGlow function in inside Pen.java
// Pen.java
public class Pen extends ImageButton {
public Pen(Context context, AttributeSet attrs) {
super(context, attrs);
}
#Override
public boolean onTouchEvent(MotionEvent event) {
System.out.println("Now I am setting Pen");
// Here I say to apply the glow effect to the image
setImageBitmap(setGlow(R.drawable.pen));
return true;
}
public Bitmap setGlow(int resourceId) {
....
}
}
Then in xml file I do :
.....
<com.example.testsetimagebmp.Pen
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="#+id/pen"
android:background="#drawable/pen" />
....
Finally in MainActivity
public class MainActivity extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
// ....
}
I think this might be the classic out of memory issue. You can try to use the bitmap of the launcher icon if you want to check whether this is true:
public class Pen extends ImageButton {
public Pen(Context context, AttributeSet attrs) {
super(context, attrs);
}
#Override
public boolean onTouchEvent(MotionEvent event) {
System.out.println("Now I am setting Pen");
// Here I say to apply the glow effect to the image
setImageBitmap(setGlow(R.mipmap.ic_launcher));
return true;
}
public Bitmap setGlow(int resourceId) {
....
}
}
If this is the problem check this link.
You can't do that because you will out of memory if you put setImageBitmap(setGlow(R.drawable.pen)) in onTouchEvent.
onTouchEvent will be triggered serval times per second.And you will create serval bitmaps in memory per second.It will out of memory before you setImageBitmap successfully.
I built the project as you post, but it works fine. It sets the Bitmap correctly. So maybe the getGlow() method doesn't work well. Make some Logs as below
public Bitmap setGlow(int resourceId) {
Bitmap bm = BitmapFactory.decodeResource(getResources(),resourceId);
Log.e("tag", "Size: " + bm.getByteCount());
return bm;
}
then check whether the return is right.
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();
}
});
}
}
I have an activity that has two classes like these:
public class StartActivity extends Activity {
.
.
.
}
public class StartView extends View {
.
.
.
}
I want to go from this Activity to another one with click on one image.Is there any method (such startActivity method in Activity Class) in View Class that I can use it in the Second Class?
Do you mean something like this:
public class startView extends View {
Activity mActivity;
Context mContext;
public startView(Activity yourActivity, Context context, AttributeSet attrs) {
// TODO Auto-generated constructor stub
super(context, attrs);
this.mActivity = yourActivity;
this.mContext = context;
init();
}
public void init()
{
ImageView iv = (ImageView) findViewById(R.id.IMAGEVIEWID);
iv.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View arg0) {
// TODO Auto-generated method stub
Intent i = new Intent(mActivity, SecondActivity.class);
mContext.startActivity(i);
}
});
}
}
My problem that my live wallpaper displays some ad in the settings screen in a linearlayout (it works perfectly), but when I send it to background (e.g.: press home button), it seems that the adview is not destroyed and that causes higher cpu usage (25-50%). If I turn off my internet connection or just remove the ad displaying code it works without spinning.
After I investigated the question, I found (https://developers.google.com/mobile-ads-sdk/docs/admob/fundamentals) that I had to destroy the adview at onDestroy, but my problem is that this should happen in the activity where I can't reach the adview. And I don't know how I could solve it so if you have any idea please help me.
My code:
AdPreference.java:
public class AdPreference extends Preference {
public AdPreference(Context context, AttributeSet attrs, int defStyle) {super (context, attrs, defStyle);}
public AdPreference(Context context, AttributeSet attrs) {super(context, attrs);}
public AdPreference(Context context) {super(context);}
public AdView adView;
#Override
protected View onCreateView(ViewGroup parent) {
// this will create the linear layout defined in ads_layout.xml
View view = super.onCreateView(parent);
// the context is a PreferenceActivity
Activity activity = (Activity)getContext();
// Create the adView
adView = new AdView(activity, AdSize.BANNER, "mybanner");
((LinearLayout)view).addView(adView);
// Initiate a generic request to load it with an ad
AdRequest request = new AdRequest();
adView.loadAd(request);
return view;
}
and my activity:
public class Prefs extends PreferenceActivity
implements SharedPreferences.OnSharedPreferenceChangeListener {
#Override
protected void onCreate(Bundle icicle) {
super.onCreate(icicle);
addPreferencesFromResource(R.xml.wallpaper_settings);
}
#Override
protected void onDestroy()
{
getPreferenceManager().getSharedPreferences().unregisterOnSharedPreferenceChangeListener(this);
super.onDestroy();
}
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key)
{
} }
I could be wrong because I am not the best at this, but it looks like you might be missing a
#Override
public void onDestroy() {
if (adView != null) {
adView.destroy();
}
super.onDestroy();
}
Or something like that. I mean, I don't see really anywhere that you tell the live wallpaper to stop the ad requests.
Like I said, I could be wrong, but those are my beliefs on the matter at hand.
I made a simple extension of CheckBoxPreference so that I could have my own custom view with an image icon to the left of the title. The code is below:
public class CustomCheckBoxPreference extends CheckBoxPreference {
private Drawable icon;
public CustomCheckBoxPreference(Context context, AttributeSet attrs) {
super(context, attrs);
TypedArray arr = context.obtainStyledAttributes(attrs, R.styleable.CustomCheckBoxPreference, 0, 0);
icon = arr.getDrawable(R.styleable.CustomCheckBoxPreference_icon);
setLayoutResource(R.layout.custom_checkbox_pref);
}
#Override
protected void onBindView(View view) {
super.onBindView(view);
ImageView prefsIcon = (ImageView) view.findViewById(R.id.prefs_icon);
prefsIcon.setImageDrawable(icon);
}
The problem is that for some reason the OnPreferenceChangeListener I set to any CustomCheckboxPreference has no effect and is not stored. I tried overriding some of the android methods for the implementation calling super and then printing a line to see what gets called. Notably callChangeListener does not get called. It is this method that leads to the callback for onPreferenceChanged. I tried throwing in a call to onPreferenceChanged inside of setChecked just to see what would happen and the OnPreferenceChangeListener is null:
getOnPreferenceChangeListener().onPreferenceChange(this, checked);
This is how I set the preferencechangelistener:
mTwitterPref.setChecked(!mTwitter.needsAuth());
mTwitterPref.setOnPreferenceChangeListener(new OnPreferenceChangeListener() {
#Override
public boolean onPreferenceChange(Preference preference, Object newValue) {
System.out.println("Twitter Preference Changed!");
if ((Boolean) newValue) {
if (mTwitter.needsAuth()) {
System.out.println("We Need To Login To Twitter!");
IntentUtils.startActivityForResult(ProfileAccountsActivity.this,
TwLoginActivity.class, ACTIVITY_OAUTH);
}
} else {
showDialog(DIALOG_LOGOUT_TWITTER);
}
return false;
}
});
I am a bit confused as to why the preferencechangelistener is not working properly as I only overwrite onBindView and the constructor; I call super in both. Any thoughts?
Set android:focusable="false" and android:clickable="false" on the CheckBox:
<CheckBox
android:id="#+android:id/checkbox"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:focusable="false"
android:clickable="false" />
More info on this thread: Clickable rows on custom ArrayAdapter
I had the same issue with custom button. I tried the solution provided by #jeanh and it works. But my button was not pressed, only area around it was highlighted. Moreover, what if you have a few buttons? Obviously, that this solution won't work. So, I decided to dig deeper and my solution was below:
xml:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#android:id/widget_frame" android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:paddingLeft="15dp"
android:paddingTop="5dp"
android:paddingRight="10dp"
android:paddingBottom="5dp"
>
<!-- define my button -->
<Button android:id="#android:id/title"
android:layout_alignParentLeft="true"
android:layout_alignParentTop="true"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:textSize="17sp"
android:typeface="sans"
android:textStyle="bold"
android:textColor="#000000"
></Button>
</RelativeLayout>
Java class:
public class ButtonPreference extends Preference {
private final String TAG = getClass().getName();
public interface ButtonListener {
public void onCustomClick();
}
public ButtonListener buttonListener;
public void setButtonListener(ButtonListener buttonListener){
this.buttonListener = buttonListener;
}
public ButtonPreference(Context context, AttributeSet attrs) {
super(context, attrs);
}
public ButtonPreference(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
#Override
protected View onCreateView(ViewGroup parent){
RelativeLayout layout = null;
try {
LayoutInflater mInflater = (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
layout = (RelativeLayout)mInflater.inflate(R.layout.button_preference, parent, false);
//FIND OUR BUTTON IN LAYOUT
Button button = (Button) layout.findViewById(android.R.id.title);
if(button!=null){
Log.e(TAG, "button found");
button.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
if(buttonListener!=null){
buttonListener.onCustomClick(); //INVOKE OUR EVENT!
}
}
});
}
}
catch(Exception e)
{
Log.e(TAG, "Error creating info preference", e);
}
return layout;
}
}
HOW TO USE IT? Simple!
public class WallpaperSettings extends PreferenceActivity {
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
addPreferencesFromResource(R.xml.prefs);
ButtonPreference defaultSettingsButton = (ButtonPreference) findPreference(EngineCore.pref+"defaultSettings");
defaultSettingsButton.setButtonListener(new ButtonListener() {
#Override
public void onCustomClick() {
Gdx.app.log("ButtonListener", "onCustomClick");
}
});
}
}
I hope it helps someone else.
I found solution !
In my case, I extends DialogPreference to my custom Dialog class,
public class SvDialogPreference extends DialogPreference
I also confuse, because in PreferenceFragment, onPreferenceChange never worked.
SvDialogPreference pref= (SvDialogPreference) findPreference("mainKey");
if( pref != null ) {
pref.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
#Override
public boolean onPreferenceChange(Preference preference, Object newValue) {
// Never execute !
}}
To resolve this. I called "super.callChangeListener" in onDialogClosed.
public class SvDialogPreference extends DialogPreference{
....
#Override
protected void onDialogClosed(boolean positiveResult) {
double InputValue = Double.parseDouble(KEY.getText().toString());
super.callChangeListener(InputValue);
super.onDialogClosed(positiveResult);
}
Now, onPreferenceChange worked fine !