I have such piece of code :)
And i want to edit text property of my custom preference layout.
But any changes made on object from getView function does not affect actual list in preference screen. any ideas? I know that i cant extend PreferenceScreen, and i cant use any other type of preference in this case, i only want to be able to edit my custom textview from my layout in code.
PreferenceScreen settings = getPreferenceManager().createPreferenceScreen(this);
settings.setLayoutResource(R.layout.mypreference);
View test = (View) settings.getView(null,getListView());
TextView text = (TextView)test.findViewById(R.id.text);
text.setText("BLA BLA");
We should be using getView(convertView, parent) but from countless unanswered questions on StackOverflow, it's obvious no one knows how to use it. So our only option is to create a custom Preference:
Create a new Class:
public class CustomPreference extends Preference {
public CustomPreference(Context context, AttributeSet attrs) {
super(context, attrs);
}
#Override
protected void onBindView(View rootView) {
super.onBindView(rootView);
View myView = rootView.findViewById(R.id.myViewId);
// do something with myView
}
}
In your xml/preferences.xml, add your custom preference:
<your.package.name.CustomPreference
android:key="custom_preference"
android:layout="#layout/custom_layout" />
Using the custom layout below:
custom_preferences_layout.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"
android:orientation="vertical">
<Button
android:id="#+id/preview_button"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:layout_marginBottom="20dp"
android:layout_marginLeft="10dp"
android:layout_marginRight="10dp"
android:text="#string/notification_preview" />
<ListView
android:id="#android:id/list"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>
Using the file above with your user preferences xml file, you can do this to access the items in your custom layout:
public class CustomPreferenceActivity extends PreferenceActivity {
#Override
protected void onCreate(Bundle bundle){
super.onCreate(bundle);
addPreferencesFromResource(R.xml.custom_preferences);
setContentView(R.layout.custom_preferences_layout);
}
private void initCustomizePreferences(){
//Button
Button button = (Button)findViewById(R.id.preview_button);
//Do something with the button.
}
}
I hope this helps :)
You need to override onBindView in your custom Preference class.
More details -
Get view of preference with custom layout
I never actually messed with the views of a PreferenceActivity, but here's something I do (usually for ListPreferences)
ListPreference listPref;
CheckBoxPreference cbPref;
// Set the summary as the current listpref value
listPref = (ListPreference) findPreference("list_pref_key");
listPref.setSummary(listPref.getEntry());
// change the title / summary / ??? of any preference
findPreference("anothe_pref_key").setTitle("New Title");
findPreference("anothe_pref_key").setSummary("New subtext for the view");
This way, even if the layout used by PreferenceActivity / PreferenceFragment change in a future version, you wont have to change anything in your code.
Related
I created button in Preferences in this way:
<PreferenceScreen>
<Preference
android:key="resetBD"
android:title="#string/ajustes_almacenamiento"
android:summary="#string/ajustes_almacenamiento_desc"
android:widgetLayout="#layout/pref_reset_bd_button" >
</Preference>
</PreferenceScreen>
layout/pref_reset_bd_button.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/submit_layout_button"
android:layout_height="wrap_content"
android:layout_width="fill_parent"
android:weightSum="10">
<Button
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/resetButton"
android:text="Reset"
android:layout_width="wrap_content"
android:onClick="submitWifiToDevice"
android:layout_height="wrap_content">
</Button>
</LinearLayout>
In PreferenceFragment I successfuly access/get the button doing this:
View footerView = ((LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE)).inflate(R.layout.pref_reset_bd_button, null, false);
resetButton= (Button) footerView.findViewById(R.id.resetButton);
But when I try to change button text
resetButton.setText("NEW ONE");
(or disable) nothing happening. What am I doing wrong?
Try this. Create a custom Preference:
public class PreferenceWidget extends Preference {
private View mView;
#Override
protected void onBindView(View view) {
super.onBindView(view);
mView = view.findViewById(R.id.resetButton);
}
public View getView() {
return mView;
}
}
Then change your preference xml
<PreferenceScreen>
<package.PreferenceWidget
android:key="resetBD"
android:title="#string/ajustes_almacenamiento"
android:summary="#string/ajustes_almacenamiento_desc"
android:widgetLayout="#layout/pref_reset_bd_button" >
</package.PreferenceWidget >
</PreferenceScreen>
Then to get the view
PreferenceWidget p = (PreferenceWidget)findPreference("resetBD");
Button b = (Button)p.getView();
b.setText("Hello");
Original answer:
You shouldn't have to inflate the View yourself. This should happen in your call to addPreferencesFromResource.
Then you can get the preference, get the preference's view, find the button view, then set the text.
Preference p = findPreference("resetBD");
View v = p.getView(null, null);
Button b = (Button)v.findViewById(R.id.resetButton);
v.setText("Hello");
Although, since no one seems to know how to use the Preference.getView function you may have to create a custom Preference object.
I have a preference screen that is populated with items from a database. I have this working by creating my own PreferenceActivity. In the activity I create DialogPreference items and add them to my PreferenceCategory To style to preference item on the screen I use a custom layout and apply it using setLayoutResource(R.layout.custom_pref_row)
This basically adds an ImageButton to the view aligned to the right of the layout. This all works fine and my preference screen shows the custom view with the button. My question is how do I attach a click listener to the button in the custom view? I was not able to find a way to get at View for the row from the PreferenceActivity. If my items were not created dynamically I might be able to do this all from XML and then reference the id or the button, but I can do that because I am creating the list dynamically.
Any suggestions on how to get a handle on the ImageButton for each item? In the end I want to configure the button to launch a delete confirmation dialog.
R.layout.custom_pref_row:
<?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="wrap_content"
android:minHeight="?android:attr/listPreferredItemHeight"
android:gravity="center_vertical"
android:paddingRight="?android:attr/scrollbarSize">
<RelativeLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="15dip"
android:layout_marginRight="6dip"
android:layout_marginTop="6dip"
android:layout_marginBottom="6dip"
android:layout_weight="1">
<TextView android:id="#+android:id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:singleLine="true"
android:textAppearance="?android:attr/textAppearanceLarge"
android:ellipsize="marquee"
android:fadingEdge="horizontal" />
<TextView android:id="#+android:id/summary"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="#android:id/title"
android:layout_alignLeft="#android:id/title"
android:textAppearance="?android:attr/textAppearanceSmall"
android:maxLines="2" />
<ImageButton android:id="#+id/pref_delete_station" android:layout_width="wrap_content" android:layout_height="wrap_content" android:src="#drawable/ic_trash_can" android:layout_alignParentRight="true" android:background="#null"></ImageButton>
</RelativeLayout>
<!-- Preference should place its actual preference widget here. -->
<LinearLayout android:id="#+android:id/widget_frame"
android:layout_width="wrap_content"
android:layout_height="fill_parent"
android:gravity="center_vertical"
android:orientation="vertical" />
</LinearLayout>
Related part of my PreferenceActivity:
DialogPreference diaPref;
for (Station mStation : sList) {
diaPref = new StationEditor(this.getPreferenceScreen().getContext(), null, this, mStation);
diaPref.setLayoutResource(R.layout.custom_pref_row);
diaPref.setTitle(mStation.getName());
diaPref.setKey(STATION_PREFIX + mStation.getId());
// add new preference
stationTypesCategory.addPreference(diaPref);
}
You can extend DialogPreference and override the onBindDialogView(View view). Inside this method you can do:
#Override
protected void onBindDialogView(View view) {
((ImageButton) view.findViewById(R.id.pref_delete_station)).setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
// TODO Auto-generated method stub
}
});
super.onBindDialogView(view);
}
Your sublcass of DialogPreference can hold any state/value related to the item it represents.
Take a look at this question about general guidelines to extend DialogPreference.
Hope this helps!
OK, Chopin got me thinking in a different direction. I did not realize that the Preference object is also responsible for how its selector appears in a Preference screen.
The setLayoutResouce() function sets the resource for the Dialog itself not the row seen in a Preference screen. This was confusing and I was incorrectly trying to use this in the preference screen to adjust the selector layout there.
The solution is to override onCreateView and return a custom layout there. To me this is counterintuitive because that method usually controls the final view in most other situations.
I alraedy subclassed my Preference (DialogPreference) so all I had to do was add the following...
#Override
protected View onCreateView (ViewGroup parent) {
LayoutInflater inflater = (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View customRow = inflater.inflate(R.layout.preferences_station_list_row, null);
((ImageButton) customRow.findViewById(R.id.pref_delete_station)).setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
Log.i("c","clicked");
}
});
customRow.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
showDialog(null);
}
});
customRow.setClickable(true);
return customRow;
}
One problem I ran into was that at first the row itself was no longer clickable but the button was. I had to add a listener on the whole view and manually call ShowDialog(). The only thing missing now is that when clicked from the Preference screen the item no longer shows a highlight. Any idea what styles I should apply so the list shows the highlight like it normally does?
I have created an xml page that holds 2 textviews and a seekbar all without ids.
The class CustomSeekBar creates these objects using the xml page as a basic structure.
You can see space for the textviews on my emulator, but I am having a hard time figuring out to set the text. Obviously I am missing something, because there is no way for the CustomSeekBar class to be able to tell which textview I want to set the text for.
How do I set the text of each individual view without giving each textview a hardcoded ID?
The reason I say without a hardcoded ID, is because if each textview is named, then when one textview's text needs to be changed, won't all the textview's texts, with that ID, change?
How would I call the specific textview ID since my customseekbar class is in a composite relationship with the activity?
Activity that calls everything.
public class ColorsActivity extends ListActivity {
/** Called when the activity is first created. */
//Array Adapter that will hold our ArrayList and display the items on the ListView
SeekBarAdaptor seekBarAdaptor;
//List that will host our items and allow us to modify that array adapter
ArrayList<CustomSeekBar> seekBarArrayList=null;
// TextView myValueText;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.seekbarlist);
//Initialize ListView
ListView lstTest= getListView();
//Initialize our ArrayList
seekBarArrayList = new ArrayList<CustomSeekBar>();
//Initialize our array adapter
seekBarAdaptor = new SeekBarAdaptor(ColorsActivity.this, R.layout.seekbars, seekBarArrayList);
CustomSeekBar red = new CustomSeekBar(this, "red", 1);
//CustomSeekBar blue = new CustomSeekBar(this, "blue");
//CustomSeekBar green = new CustomSeekBar(this, "green");
//Set the above adapter as the adapter of choice for our list
lstTest.setAdapter(seekBarAdaptor);
seekBarArrayList.add(red);
//seekBarArrayList.add(blue);
//seekBarArrayList.add(green);
Amarino.connect(this, "00:11:11:21:05:53");
}
}
CustomSeekBar class
public class CustomSeekBar implements SeekBar.OnSeekBarChangeListener {
Context myContext;
TextView myValue;
TextView myLabel;
SeekBar mySeekBar;
CustomSeekBar(Context context, String label, int ID){
myContext = context;
myValue = new TextView(myContext);
mySeekBar = new SeekBar(myContext);
myValue.setText(label);
mySeekBar.setOnSeekBarChangeListener(this);
}
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromTouch) {
myValue.setText(progress);
Amarino.sendDataToArduino(myContext, "00:11:11:21:05:53", 'A', progress);
}
public void onStopTrackingTouch(SeekBar seekBar){
}
public void onStartTrackingTouch (SeekBar seekBar){
}
}
seekbarlist.xml holds my list view for the custom list
<?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:id="#android:id/list"
android:layout_width="fill_parent"
android:layout_height="wrap_content">
</ListView>
</LinearLayout>
seekbars.xml is the structure of each custom list item (CustomSeekBar)
<?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"
android:id="#+id/seekBarLayout">
<TextView
android:gravity="center_horizontal"
android:background="#aa0000"
android:layout_width="wrap_content"
android:layout_height="0dp"
android:layout_weight="1"
/>
<TextView
android:gravity="center_horizontal"
android:background="#aa0000"
android:layout_width="wrap_content"
android:layout_height="0dp"
android:layout_weight="1"
/>
<SeekBar
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:max="255"/>
</LinearLayout>
why would you have to name them the same ID name why not
#+id/textview1 and #+id/textview2 and then reference the two text boxes in your code I don't understand what is stopping you?
You can use IDs as #bmporter12 said. You can have duplicate IDs, provided that Android has a place to start looking from when you tell it to findViewById. So, in your adapter, in getView(), you would inflate your new row from seekbars.xml and then do row.findViewById(R.id.textView1) and row.findViewById(R.id.textView2).
If you need to set it from outside the adapter, then depending on where you're getting the signal to set a TextView, either your CustomSeekBar could ask the Activity for its entry at a particular position in the adapter or it could use the View parameter passed in an onClick callback.
So I have a CustomView which is extended from View. And I have a linear layout from XML.
The XML named example:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res/jembalang.comfest.game"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<jembalang.compfest.game.GameThread
android:id="#+id/game_view"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
</jembalang.compfest.game.GameThread>
<Button
android:text="Button"
android:id="#+id/button1"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
</Button>
</LinearLayout>
And the code using the xml
setContentView(R.layout.cobagabung);
gameView = (GameThread) Jembalang.this.findViewById(R.id.game_view);
gameView.setFocusable(true);
gameView.setFocusableInTouchMode(
gameView.start();
I added the GameThread constructor if that's helping
public class GameThread extends View implements Runnable,OnKeyListener{
public GameThread(Context context,AttributeSet attr){
super(context, attr);
...
}
public GameThread(Context context) {
super(context);
...
}
I think there is something wrong with my way doing it, because the findViewById returns null
How should I do to make my CustomView (GameThread at this example) to be able inserted into xml?
Your line should read:
gameView = (GameThread) Jembalang.this.findViewById(R.id.game_view);
you are passing the id of a layout rather than that of the view you have created.
The rest of your code looks fine
I don't know what Jembalang is but I think you should remove that.
gameView = (GameThread) findViewById(R.id.game_view);
You say your layout file is called "example.xml", but you call setContentView(R.layout.cobagabung). Thus, your view is initialized from "cobagabung.xml".
Make sure you use the same identifier for layout file name and setContentView call, like
setContentView(R.layout.example);
I come to you on bended knee, question in hand. I am relatively new to Android, so pardon any sacrilegious things I might say.
Intro: I have several layouts in the app, that all have to include a common footer. This footer has some essential buttons for returning to the home page, logging out, etc.
I managed to get this footer to appear in all the requisite pages with the help of the Include and Merge tags. The issue lies in defining on click listeners for all the buttons. Although I can define the listeners in every activity associated with screens that include the footer layout, I find that this becomes terribly tedious when the number of screens increases.
My question is this: Can I define a button click listener that will work across the application, which can be accessed from any screen with the use of the android:onClick attribute of the Button?
That is to say, I would like to define the button click listener once, in a separate class, say FooterClickListeners, and simply name that class as the listener class for any button clicks on the footer. The idea is to make a single point of access for the listener code, so that any and all changes to said listeners will reflect throughout the application.
I had the same problem with a menu which I used in several layouts. I solved the problem by inflating the layout xml file in a class extending RelativeLayout where I then defined the onClickListener. Afterwards I included the class in each layout requiring the menu. The code looked like this:
menu.xml
<?xml version="1.0" encoding="utf-8"?>
<merge xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<ImageButton android:id="#+id/map_view"
android:layout_alignParentTop="true"
android:layout_alignParentLeft="true"
android:src="#drawable/button_menu_map_view"
android:background="#null"
android:scaleType="fitCenter"
android:layout_height="#dimen/icon_size"
android:layout_width="#dimen/icon_size">
</ImageButton>
<ImageButton android:id="#+id/live_view"
android:layout_alignParentTop="true"
android:layout_alignParentLeft="true"
android:src="#drawable/button_menu_live_view"
android:background="#null"
android:scaleType="fitCenter"
android:layout_height="#dimen/icon_size"
android:layout_width="#dimen/icon_size">
</ImageButton>
<ImageButton android:id="#+id/screenshot"
android:layout_alignParentTop="true"
android:layout_alignParentRight="true"
android:src="#drawable/button_menu_screenshot"
android:background="#null"
android:scaleType="fitCenter"
android:layout_height="#dimen/icon_size"
android:layout_width="#dimen/icon_size">
</ImageButton>
</merge>
MenuView.java
public class MenuView extends RelativeLayout {
private LayoutInflater inflater;
public MenuView(Context context, AttributeSet attrs) {
super(context, attrs);
inflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
inflater.inflate(R.layout.menu, this, true);
((ImageButton)this.findViewById(R.id.screenshot)).setOnClickListener(screenshotOnClickListener);
((ImageButton)this.findViewById(R.id.live_view)).setOnClickListener(liveViewOnClickListener);
((ImageButton)this.findViewById(R.id.map_view)).setOnClickListener(mapViewOnClickListener);
}
private OnClickListener screenshotOnClickListener = new OnClickListener() {
public void onClick(View v) {
getContext().startActivity(new Intent(getContext(), ScreenshotActivity.class));
}
};
private OnClickListener liveViewOnClickListener = new OnClickListener() {
public void onClick(View v) {
getContext().startActivity(new Intent(getContext(), LiveViewActivity.class));
}
};
private OnClickListener mapViewOnClickListener = new OnClickListener() {
public void onClick(View v) {
getContext().startActivity(new Intent(getContext(), MapViewActivity.class));
}
};
}
layout.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/main"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<SurfaceView android:id="#+id/surface"
android:layout_width="fill_parent"
android:layout_weight="1"
android:layout_height="fill_parent">
</SurfaceView>
<!-- some more tags... -->
<com.example.inflating.MenuView
android:layout_width="fill_parent"
android:layout_height="fill_parent" />
</RelativeLayout>
with the <com.example.inflating.MenuView /> tag, you are now able to reuse your selfwritten Layout (incl onClickListener) in other layouts.
This is something that is getting added to roboguice in the near the future. It will allow you to build controller classes for things like titlebar's and footers and have the events autowired for you.
Checkout http://code.google.com/r/adamtybor-roboguice/ for the initial spike.
Basically if you are using roboguice you can define a component for footer and just inject that footer component into each activity.
Unfortunately you still have to add the controller to every activity, just like you did with the include layout, but the good news is everything gets wired up for you and all your logic stays in a single class.
Below is some pseudo code of some example usage.
public class FooterController {
#InjectView(R.id.footer_button) Button button;
#Inject Activity context;
#ContextObserver
public void onViewsInjected() {
button.setOnClickListener(new OnClickListener() {
void onClick() {
Toast.makeToast(context, "My button was clicked", Toast.DURATION_SHORT).show();
}
});
}
}
public class MyActivity1 extends RoboActivity {
#Inject FooterController footer;
}
public class MyActivity2 extends RoboActivity {
#Inject FooterController footer;
}
The solution as you describe is impossible, sorry. But you can have common parent activity for all your activities that use the footer. In the activity just provide handler methods for your footer buttons, then just inherit from it every time you need to handle the footer actions.