Single inflation of a style to be used multiple times - android

I want to inflate an item once and use it in a loop. I currently have a solution, but there is most likely a better way. Also, the program won't run unless there is view.removeView call, which makes sense, but seems hazardous if I ever want to add catBtn later in the app).
Existing code:
LinearLayout col1 = (LinearLayout)findViewById(R.id.col1);
for(int i = 0; i < 10; ++i) {
LinearLayout assets = (LinearLayout)this.getLayoutInflater().inflate(R.layout.assets, null);
Button btn = (Button)assets.findViewById(R.id.catBtn);//new Button(this);
assets.removeView(btn);
col1.addView(btn);
}
Existing layout.assets
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:weightSum="1"
android:id="#+id/assets">
<ImageView android:focusable="true"
android:id="#+id/thumb"
android:background="#drawable/selectable"
android:layout_marginBottom="20dip"
android:src="#drawable/icon" android:layout_height="140dip" android:layout_width="250dip"/>
<Button android:id="#+id/catBtn"
android:layout_height="wrap_content"
android:background="#drawable/selectable"
android:text="Cat Button"
android:layout_width="120dip"
android:textSize="16dip"></Button>
</LinearLayout>

You could pass false as the last parameter to the inflate method
LayoutInflator.from(context).inflate(res, parent, false);
Which causes the inflated view to be attached to nothing. That way you don't have to remove anything. That gets rid of the assets.removeView() issue. But I think this still might be wasteful.
It looks like you just want some buttons:
<Button android:id="#+id/catBtn"
android:layout_height="wrap_content"
android:background="#drawable/selectable"
android:text="Cat Button"
android:layout_width="120dip"
android:textSize="16dip">
Let's extract that to a style:
<resources>
<declare-stylable android:name="awesome_button">
<attr android:name="awesomeButtonStyle" android:type="reference"/>
</declare-stylable>
<style android:name="AwesomeButton">
<item android:name="android:layout_height">wrap_content</item>
<item android:name="android:background">#drawable/selectable</item>
<item android:name="android:layout_width">120dp</item>
<item android:name="android:text">Cat Button</item>
<item android:name="android:textSize">16sp</item>
</style>
<style android:name="Theme.WithAwesomeButtons" parent="#android:style/Theme">
<item android:name="awesomeButtonStyle">#style/AwesomeButton</item>
</style>
<resources>
OK now we are rolling with style ;) (sorry couldn't resist). Now let's configure your Activity, inside the AndroidManifest.xml:
<activity android:name=".MyCatBtnActivity"
... Whatever else is in your activity
android:theme="#style/Theme.WithAwesomeButtons"/>
OK now within your loop:
for (int i=0; i<10; i++) {
// Let's get rid of the LayoutInflator (unless you want to use an xml layout
// in which case, make awesomeButton.xml and have it just have a button in it
// with attribute style="?awesomeButtonStyle").
Button button = new Button(this, null, R.attr.awesome_button.awesomeButtonStyle));
// Let's tag them with the integer counter so we can id them later
// You can set id, but there is a slight chance it will not be unique
// within the hierarchy. Later on you can either use col1.getChildView(index) to scan
// and look for these tags (or store them in a local array if col1 holds a lot of views)
// Then you can also evaluate the tag whenever you are referring to a button from
// within an OnClickListener or any View listener for that matter.
button.setTag(Integer.valueOf(i));
col1.add(button);
}
I think this is sort of what you are trying to achieve.

Related

Android Progress bar inside menu item is not updating properly

I have two sensor connected with my android app. Both apps are connecting over BLE and I get two types of data from these sensors.
Sensor Data
Power Value
To show the power status, I followed a tutorial to create the progressbar inside actionbar menu as follows.
theme.xml
<!-- Power Progress Theme -->
<style name="PowerProgressBar">
<item name="android:indeterminateOnly">false</item>
<item name="android:progressDrawable">#drawable/ic_action_power_progress</item>
</style>
layout_power_progress.xml
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
style="#android:style/Widget.ActionButton">
<ProgressBar
android:id="#+id/powerProgress"
style="#style/PowerProgressBar"
android:layout_width="24dp"
android:layout_height="24dp"
android:visibility="visible"
android:layout_gravity="center" />
</FrameLayout>
menu/main.xml
<item
android:id="#+id/action_power_sensor_1"
android:title="#string/action_power_sensor_1"
app:actionLayout="#layout/layout_power_progress"
app:showAsAction="always" />
<item
android:id="#+id/action_power_sensor_2"
android:title="#string/action_power_sensor_2"
app:actionLayout="#layout/layout_power_progress"
app:showAsAction="always" />
override fun onPrepareOptionsMenu(menu: Menu?): Boolean {
/*---------- Sensor 1 ---------------*/
sensor1PowerProgressView = menu?.findItem(R.id.action_power_sensor_1)?.actionView?.findViewById(R.id.powerProgress)
sensor1PowerProgressView ?.max = 5
sensor1PowerProgressView ?.progress = sensor1Power
/*---------- Sensor 2 ---------------*/
sensor2PowerProgressView = menu?.findItem(R.id.action_power_sensor_2)?.actionView?.findViewById(R.id.powerProgress)
sensor2PowerProgressView ?.max = 100
sensor2PowerProgressView ?.progress = sensor2Power
return super.onPrepareOptionsMenu(menu)
}
/*--- Code where the power is getting updated ----*/
sensorViewModel.getSensor1Power().observe(this) {
sensor1Power = it
if (sensor1PowerPrev!=sensor1Power) {
sensor1PowerPrev = sensor1Power
invalidateOptionsMenu()
}
}
sensorViewMode2.getSensor2Power().observe(this) {
sensor1Power = it
if (sensor2PowerPrev!=sensor2Power) {
sensor2PowerPrev = sensor2Power
invalidateOptionsMenu()
}
}
I am updating these power values from sensorViewModel. I see the values inside the sensorViewModel getting updated properly upon sensor connect and disconnect. I am logging both values (sensor1Power & sensor2Power ) inside the onPrepareOptionsMenu and I see the values getting logged there properly.
Now after spending whole 2 weeks on this issue I can't figure out why sensor1PowerProgressView and sensor2PowerProgressView are not getting updated on disconnect. They retain the last value.
Anyone, can point out what am I doing incorrectly. if you need any more details, I can share upon request.

Android how to create simple custom UI elements

I would like to create simple custom UI elements in Android like the ones from the screenshot:
The light bulb should always have the same size but the rectangle should vary in the width. One option of doing this is to use Canvas elements. But I would like to ask whether there is also an easier approach for doing this. Is it possible to maybe only do this by using XML files? I would like to use these UI elements then in the LayoutEditor like e.g. a TextView where I can adjust the widht and height either in the XML layout file or programmatically.
Any idea how I can do that in an easy way?
Update: I tried the suggested approach from Cheticamp and I have the following code inside my Fragment:
public class Test extends Fragment implements Runnable {
/*
Game variables
*/
public static final int DELAY_MILLIS = 100;
public static final int TIME_OF_A_LEVEL_IN_SECONDS = 90;
private int currentTimeLeftInTheLevel_MILLIS;
private Handler handler = new Handler();
private FragmentGameBinding binding;
private boolean viewHasBeenCreated = false;
public Test() {
// Required empty public constructor
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
binding = FragmentGameBinding.inflate(inflater, container, false);
getActivity().setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
container.getContext();
viewHasBeenCreated = true;
startRound();
return binding.getRoot();
}
public void startRound () {
currentTimeLeftInTheLevel_MILLIS =TIME_OF_A_LEVEL_IN_SECONDS * 1000;
updateScreen();
handler.postDelayed(this, 1000);
}
private void updateScreen() {
binding.textViewTimeLeftValue.setText("" + currentTimeLeftInTheLevel_MILLIS/1000);
/*
IMPORTANT PART: This should create a simple custom UI element but it creates an error
*/
View view = new View(getActivity());
view.setLayoutParams(new ViewGroup.LayoutParams(100, 100));
Drawable dr = ContextCompat.getDrawable(getActivity(),R.drawable.light_bulb_layer_list);
view.setBackground(dr);
ConstraintLayout constraintLayout = binding.constraintLayout;
ConstraintSet constraintSet = new ConstraintSet();
constraintSet.clone(constraintLayout);
constraintSet.connect(view.getId(),ConstraintSet.BOTTOM, ConstraintSet.PARENT_ID,ConstraintSet.BOTTOM,0);
constraintSet.connect(view.getId(),ConstraintSet.TOP,ConstraintSet.PARENT_ID ,ConstraintSet.TOP,0);
constraintSet.connect(view.getId(),ConstraintSet.LEFT,ConstraintSet.PARENT_ID ,ConstraintSet.LEFT,0);
constraintSet.connect(view.getId(),ConstraintSet.RIGHT,ConstraintSet.PARENT_ID ,ConstraintSet.RIGHT,0);
constraintSet.setHorizontalBias(view.getId(), 0.16f);
constraintSet.setVerticalBias(view.getId(), 0.26f);
constraintSet.applyTo(constraintLayout);
}
private void countDownTime(){
currentTimeLeftInTheLevel_MILLIS = currentTimeLeftInTheLevel_MILLIS -DELAY_MILLIS;
updateScreen();
}
#Override
public void run() {
if(viewHasBeenCreated) {
countDownTime();
}
}
}
Unfortunately, this code leads to a "java.lang.NullPointerException: Attempt to invoke virtual method 'boolean android.content.Context.isUiContext()' on a null object reference". It is thrown by the line View view = new View(getActivity());. Here is the complete error message:
E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.example.game, PID: 12176
java.lang.NullPointerException: Attempt to invoke virtual method 'boolean android.content.Context.isUiContext()' on a null object reference
at android.view.ViewConfiguration.get(ViewConfiguration.java:502)
at android.view.View.<init>(View.java:5317)
at com.example.game.Test.updateScreen(Test.java:72)
at com.example.game.Test.countDownTime(Test.java:91)
at com.example.game.Test.run(Test.java:97)
at android.os.Handler.handleCallback(Handler.java:938)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:223)
at android.app.ActivityThread.main(ActivityThread.java:7656)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:592)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:947)
Any idea what the problem is? Without the custom UI element the Fragment works fine.
Use a TextView. The light bulb can be a left compound drawable. Set the background to a rounded rectangle shape drawable. This can all be specified in XML. See TextView.
This can also be accomplished with a LayerList drawable if text is not wanted. (The TextView solution also works without text - just set the text to "" or null.)
<layer-list>
<item>
<shape android:shape="rectangle">
<corners android:radius="5dp" />
<solid android:color="#FF9800" />
</shape>
</item>
<item
android:drawable="#drawable/ic_baseline_lightbulb_24"
android:width="48dp"
android:height="48dp"
android:gravity="left|center_vertical" />
</layer-list>
The layer list is set as a background to a simple View.
<View
android:layout_width="250dp"
android:layout_height="56dp"
android:layout_marginStart="16dp"
android:layout_marginTop="16dp"
android:layout_marginEnd="16dp"
android:layout_marginBottom="16dp"
android:background="#drawable/light_bulb_layer_list" />
To create the View in code:
View view = new View(context);
view.setLayoutParams(new ViewGroup.LayoutParams(width, height));
Drawable dr = ContextCompat.getDrawable(context,R.drawable.light_bulb_layer_list)
view.setBackground(dr);
Sure thing.
In this case a simple xml file like so would suffice. Let's name it something.xml inside the layout folder.
<LinearLayout ...>
<ImageView ...>
</LinearLayout>
In another layout xml file you may just:
<ConstraintLayout ...>
<include android:id="#+id/something"" layout="#layout/something" android:layout_width="70dp">
</ConstraintLayout>
See Reusing layouts
If you'd like to get a children you can always get them by using findViewById on your Activity or Fragment. If you're using Databinding or Viewbinding it just gets better: They'll appear as fields in the XBinding class that was generated out of the XML file
Hi VanessaF, going a little bit further with the clarifications you asked in the comments:
<include />
The <include /> tag is a special XML tag that we can use in our Android XML layout files to indicate that where we placed the <include/> we'd like it to be replaced by some other XML determined via the layout attribute inside the <include /> tag.
Here's an example:
Considering layout/example.xml
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Hello!"/>
And considering layout/parent.xml
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<Button .../>
<include layout="#layout/example"/>
<ImageView android:drawable="#drawable/ic_send"/>
</LinearLayout>
Whenever I use R.layout.parent somewhere (for example in setContent from the Activity the view that would get generated would be as follows:
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<Button .../>
<!-- PLEASE NOTICE THAT <include/> IS GONE -->
<!-- AND HAS BEEN REPLACED WITH THE CONTENTS the specified layout -->
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Hello!"/>
<ImageView android:drawable="#drawable/ic_send"/>
</LinearLayout>
Effectively re-using the layout without writing a full-blown custom view.
Notice: All attributes you specify inside the <include/> tag will effectively override the others specified inside the layout file. Let me illustrate this using an example:
Consider again layout/example.xml. Notice that this time the TextView will shrink to the size of the text both in height and width.
<TextView
android:text="Hello!"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
/>
And consider the parent: layout/parent.xml. Notice that I am setting the attributes android:layout_width and android:layout_height.
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<include
layout="#layout/example"
android:layout_width="match_parent"
android:layout_height="match_parent"
/>
</LinearLayout>
In this case, when Android replaces <include/> for the contents of #layout/example it will also set android:layout_width="match_parent" and android:layout_height="match_parent" because they were specified on the <include/> tag effectively ignoring the original attributes set inside layout/example.xml (which were set to "wrap_content")
I suggest reading Custom View Components from the official Android documentation. In fact, you should become very familiar with this documentation for everything you do with Android apps.

Android dataBinding - How to get resource id of a View in xml

In Android Studio my data binding itself works and is set up fine. I have a boolean defined like this:
<resources>
<bool name="showAds">false</bool>
</resources>
and in a layout.xml file i would like to referenced this boolean (which works fine) but i want to assign a id based on this boolean. Let me show you what i am trying to accomplish:
I have a button that is in a relativeLayout tag and depending on this boolean i would like to reposition the button. So I have this:
<Button
android:id="#+id/startButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="64dip"
****************
android:layout_below="#{#bool/showAds ? #+id/adone : #+id/title_main}"
****************
android:layout_centerHorizontal="true"
android:textColor="#0080FF"
android:text="#string/start_btn_title" />
See what i want to to do? I want to layout the button below a layout called adone if the showAds boolean is true, otherwise place it below a layout called title_main. Whats the syntax for this as what I have here is not compiling. I get a compile error:
expression expected after the second # sign
The above is the same problem as in How to get dimensions from dimens.xml
None of the LayoutParams attributes have built-in support. As answered in the linked article, data binding of LayoutParams was thought to be too easy to abuse so it was left out of the built-in BindingAdapters. You are not abusing it, so you should add your own.
#BindingAdapter("android:layout_below")
public static void setLayoutBelow(View view, int oldTargetId, int newTargetId) {
RelativeLayout.LayoutParams layoutParams = (RelativeLayout.LayoutParams)
view.getLayoutParams();
if (oldTargetId != 0) {
// remove the previous rule
layoutParams.removeRule(RelativeLayout.BELOW);
}
if (newTargetId != 0) {
// add new rule
layoutParams.addRule(RelativeLayout.BELOW, newTargetId);
}
view.setLayoutParams(layoutParams);
}
As an aside, the #+id/adone in the binding syntax will not create the id. You should create the id in the View you're binding to.

Change TimePicker text color

I've been trying to change the textcolor of my timepicker. But I can't find where the parent style is located. I've tried both
<style name="MyTimePicker" parent="#android:style/Widget.Holo.TimePicker">
<item name="android:textColor">#color/text</item>
</style>
and
<style name="MyTimePicker" parent="#android:style/Widget.TimePicker">
<item name="android:textColor">#color/text</item>
</style>
My minSdkVersion is 15. My targetSdkVersion is 20. I have rebuilded and cleaned my project.
I think I've been through every similar question on SO and none of them really have provided a solution for me. The only answer that might work is using some sort of library, but I'm not a big fan of that solution. Is the path to the parent something different from what I'm using, because I'm pretty sure I should be able to access it somehow?
Edit
This is how the theme is applied;
<TimePicker
style="#style/MyTimePicker"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="#+id/timePicker"
android:layout_below="#+id/textView"
android:layout_centerHorizontal="true" />
On a note this is the error I receive (forgot to place it before):
Error:Error retrieving parent for item: No resource found that matches the given name '#android:style/Widget.Holo.TimePicker'.
Edit 2
A couple of the questions I've viewed to try to solve this:
How can I override TimePicker to change text color - I think this question gets as close to an answer, but I'm not entirely sure what I need to do? Do I need to import the android TimePicker style into my project?
https://stackoverflow.com/questions/24973586/set-textcolor-for-timepicker-in-customized-app-theme - No answer is given.
How can i change the textcolor of my timepicker and datepicker? - Tried the 0 votes answer, but it didn't work.
How to change the default color of DatePicker and TimePicker dialog in Android? - Again can't find the TimePicker in a similar way.
Android - How do I change the textColor in a TimePicker? - Again, can't find the actual TimePicker parent.
These are probably the best questions/answers to my problem, but none of them help me. It would be nice to get a definitive answer on this.
I have combined Paul Burke's Answer and Simon's Answer to succesfully edit the text colour of the TimePicker.
Here's how it is accomplished:
TimePicker time_picker; //Instantiated in onCreate()
Resources system;
private void set_timepicker_text_colour(){
system = Resources.getSystem();
int hour_numberpicker_id = system.getIdentifier("hour", "id", "android");
int minute_numberpicker_id = system.getIdentifier("minute", "id", "android");
int ampm_numberpicker_id = system.getIdentifier("amPm", "id", "android");
NumberPicker hour_numberpicker = (NumberPicker) time_picker.findViewById(hour_numberpicker_id);
NumberPicker minute_numberpicker = (NumberPicker) time_picker.findViewById(minute_numberpicker_id);
NumberPicker ampm_numberpicker = (NumberPicker) time_picker.findViewById(ampm_numberpicker_id);
set_numberpicker_text_colour(hour_numberpicker);
set_numberpicker_text_colour(minute_numberpicker);
set_numberpicker_text_colour(ampm_numberpicker);
}
private void set_numberpicker_text_colour(NumberPicker number_picker){
final int count = number_picker.getChildCount();
final int color = getResources().getColor(R.color.text);
for(int i = 0; i < count; i++){
View child = number_picker.getChildAt(i);
try{
Field wheelpaint_field = number_picker.getClass().getDeclaredField("mSelectorWheelPaint");
wheelpaint_field.setAccessible(true);
((Paint)wheelpaint_field.get(number_picker)).setColor(color);
((EditText)child).setTextColor(color);
number_picker.invalidate();
}
catch(NoSuchFieldException e){
Log.w("setNumberPickerTextColor", e);
}
catch(IllegalAccessException e){
Log.w("setNumberPickerTextColor", e);
}
catch(IllegalArgumentException e){
Log.w("setNumberPickerTextColor", e);
}
}
}
Please note that this answer might be outdated by now. I ran into this a while ago with something that might have been buggy (see my question for more details). Otherwise you should probably follow Vikram's answer.
Not sure why you would need to dive into Java Reflection API for this. Its a simple styling matter. The attribute that you need to override is: textColorPrimary.
<style name="AppTheme" parent="#android:style/Theme.Holo.Light">
....
<item name="android:textColorPrimary">#ff0000</item>
</style>
If you're using the TimePicker inside a Dialog, override android:textColorPrimary in the dialog's theme.
That's about it.
A TimePicker is really just two NumberPickers. Looking into the Widget.NumberPicker style and layout, you'll find the it uses
#style/TextAppearance.Large.Inverse.NumberPickerInputText
Unfortunately, TextAppearance.Large.Inverse.NumberPickerInputText doesn't use one of the attributes that you can set in your theme. So you have two options:
Copy the necessary classes to make your own version of NumberPicker and TimePicker. (You might be able to extract something from libraries like HoloEverywhere)
Use hacks.
If you want to go the second route, you can do this:
private int mNumberPickerInputId = 0;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Resources system = Resources.getSystem();
// This is the internal id of the EditText used in NumberPicker (hack)
mNumberPickerInputId =
system.getIdentifier("numberpicker_input", "id", "android");
// just used for full example, use your TimePicker
TimePicker timePicker = new TimePicker(this);
setContentView(timePicker);
final int hourSpinnerId =
system.getIdentifier("hour", "id", "android");
View hourSpinner = timePicker.findViewById(hourSpinnerId);
if (hourSpinner != null) {
setNumberPickerTextColor(hourSpinner, Color.BLUE);
}
final int minSpinnerId =
system.getIdentifier("minute", "id", "android");
View minSpinner = timePicker.findViewById(minSpinnerId);
if (minSpinner != null) {
setNumberPickerTextColor(minSpinner, Color.BLUE);
}
final int amPmSpinnerId =
system.getIdentifier("amPm", "id", "android");
View amPmSpinner = timePicker.findViewById(amPmSpinnerId);
if (amPmSpinner != null) {
setNumberPickerTextColor(amPmSpinner, Color.BLUE);
}
}
private void setNumberPickerTextColor(View spinner, int color) {
TextView input = (TextView) spinner.findViewById(mNumberPickerInputId);
input.setTextColor(color);
}
EDIT
Upon further investigation, this hack doesn't really work well. It won't allow you to change the color of the NumberPicker above/below values. The color also resets after the use interacts with it. It seems that your only option will be to create your own copies of the necessary classes (option #1 above).
Adding to Vikram answer , Set the theme to timePicker do not set it as style
in styles.xml
<style name="timePickerOrange">
<item name="android:textColorPrimary">#color/orange</item> <!-- color for digits -->
<item name="android:textColor">#color/orange</item> <!-- color for colon -->
<item name="android:colorControlNormal">#color/orange</item> <!-- color for (horizontal) delimeters -->
</style>
and for timePicker
<TimePicker
android:theme="#style/timePickerOrange"
android:id="#+id/timePicker_defaultTime"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:fontFamily="#font/hero_regular"
android:timePickerMode="spinner"
app:fontFamily="#font/hero_regular" />
#Vikram is right
This is so simple.You can try this way
<style name="abirStyle" parent="#android:style/Theme.Holo.Light.Dialog.NoActionBar">
<item name="android:textColor">#color/colorPrimary</item>
<item name="android:background">#null</item>
<item name="android:textColorPrimary">#color/colorPrimary</item>
</style>
You can try to set android:textColorPrimaryInverse and android:textColorSecondaryInverse. That should do the job without using Reflection.
<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
<item name="android:textColorPrimaryInverse">#android:color/black</item>
<item name="android:textColorSecondaryInverse">#color/colorLightGrey</item>
</style>
For those using the spinner, this is an easy way to change the colors and even provides the appropriate shadow effect of the previous and next numbers. Spinner requires a theme because most of the default xml attributes for colors only effect the clock mode.
styles.xml
<style name="timepicker">
<item name="android:textColorSecondary">#color/white </item>
<item name="android:textColorPrimary">#color/white </item>
</style>
xml layout file
<TimePicker
android:id="#+id/timePicker1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:theme="#style/timepicker"
android:layout_below="#id/line1"
android:layout_centerHorizontal="true"
android:padding="15dp"
android:timePickerMode="spinner" />

ViewStub reinflates last view inflated on first pass of onCreate

I have a bug with my activity.
I have three view stubs in my linear layout like so -
<ViewStub
android:id="#+id/index_1"
android:layout="#layout/index_edittext"
android:layout_width="fill_parent"
android:layout_height="wrap_content" />
<ViewStub
android:id="#+id/index_2"
android:layout="#layout/index_edittext"
android:layout_width="fill_parent"
android:layout_height="wrap_content" />
<ViewStub
android:id="#+id/index_3"
android:layout="#layout/index_edittext"
android:layout_width="fill_parent"
android:layout_height="wrap_content" />
my onCreate conditionally checks what to inflate:
for (int i = 0; i < 3; i++) {
int id = convertIndexToId(i); //will turn i into R.id.index_1
ViewStub stub = findViewById(id);
if (bShouldBeSpinner) {
stub.setLayoutResource(R.layout.index_spinner);
View root = stub.inflate();
Spinner spin = (Spinner)root.findViewById(R.id.my_spinner);
spinner.setAdapter(adapter);
spinner.setSelection(0);
}
else {
stub.setLayoutResource(R.layout.index_edittext);
View root = stub.inflate();
EditText et = (EditText)root.findViewById(R.id.my_edittext);
//et.phoneHome(); just kidding
et.setText(String.valueOf(System.currentTimeMillis()));
}
}
I force bShouldBeSpinner to false. The output of the edittext's is as follows:
1300373517172
1300373517192
1300373517221
However, when I rotate the screen and onCreate is called a second time the output is this:
1300373517221
1300373517221
1300373517221
Initially that made me think you should only inflate the view once, and the heirarchy is kept inbetween onCreate's... however when i only run it the first time the second time no views are shown for the stubs.
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<Spinner style="#style/SearchInput" android:id="#+id/my_spinner" />
</LinearLayout>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<EditText style="#style/SearchInput" android:id="#+id/my_edittext" />
</LinearLayout>
I feel the documentation is assuming something that I did not notice or am missing. Does anyone see what I am doing wrong?
EDIT
I added to the view stubs android:inflatedId="index_1_root"... etc
it is the strangest thing, when I add these lines after the for loop:
EditText v = indexRoot1.findViewById(R.id.index_edit_text);
Log.d(TAG, "EditTExt: " + v);
EditText v2 = indexRoot2.findViewById(R.id.index_edit_text);
Log.d(TAG, "EditTExt: " + v2);
the output says (I believe) they are references to different EditTexts.
EditTExt: android.widget.EditText#47210fe8
EditTExt: android.widget.EditText#47212ba8
So they are getting inflated again, but the text is set to what the last edittext was set to on the first pass.
There may be some issues when recreating views of different types with the same id.
ViewStub is replaced by inflated view.
I suggest using
setInflatedId(int inflatedId)
to distinguish inflated views.
Hope that help.
Instead of using ViewStubs, I added an id to the root of those stubs (android:id="index_roots") and used
view.addView( (isSpinner) ?
new Spinner(this) : new EditText(this) );
to fix this problem, I will however not accept this answer right away, I'll allow others to answer using the method I was going for.

Categories

Resources