I have to develop an android application.
I have created one layout file that uses another layout file using the include tag.
<include
android:id="#+id/footer"
android:layout_width="match_parent"
android:layout_height="60dp"
android:layout_alignParentBottom="true"
layout="#layout/footer_tabs" />
<include
android:id="#+id/footer1"
android:layout_width="match_parent"
android:layout_height="60dp"
android:layout_alignParentBottom="true"
layout="#layout/footertabs" />
I would like to show the included layout when a response is null, otherwise I would like to hide the layout and show the other. Here is what I have so far:
footertabs = (RelativeLayout) findViewById(R.id.footertab);
footer_tabs = (RelativeLayout) findViewById(R.id.footer_tab);
if (Constants.response==null) {
footertabs.setVisibility(View.VISIBLE);
footer_tabs.setVisibility(View.GONE);
}
else
{
footertabs.setVisibility(View.GONE);
footer_tabs.setVisibility(View.VISIBLE);
}
But I'm getting the following error:
07-15 17:19:09.893: E/AndroidRuntime(15143): Caused by: java.lang.NullPointerException
07-15 17:19:09.893: E/AndroidRuntime(15143): at com.example.androidbestinuk.HomePage.onCreate(HomePage.java:56)
Please help me debug this error.
you should change
footertabs = (RelativeLayout) findViewById(R.id.footertab);
footer_tabs = (RelativeLayout) findViewById(R.id.footer_tab);
with
footertabs = (RelativeLayout) findViewById(R.id.footer);
footer_tabs = (RelativeLayout) findViewById(R.id.footer1);
Well it appears to me that you're using the wrong id's. You're getting a null pointer somewhere (I'm not sure where because there are no line numbers), but I see in your xml you have id's, footer and footer1, but in your code you are trying to find elements with id's footertab, and footer_tab. You should make these id's match.
Using ViewBinding and Kotlin, it can be achieved this way
binding.yourInclude.root.visibility = View.GONE
For specific views inside <include> tag use code below
binding.yourInclude.textView.visibility = View.GONE
Related
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.
I'm trying to get android:layout_marginEnd="-6dp" to change to 2dp conditionally in:
<FrameLayout
android:id="#+id/wifi_combo"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:layout_marginEnd="-6dp"
>
My condition is:
if (mSignalClusterStyle == STYLE_ALWAYS) {
mMobileType.setVisibility(View.VISIBLE);
} else if (mWifiVisible) {
mMobileType.setVisibility(View.GONE);
}
I would like to add a line to the if statement that overrides the -6dp with 2dp in the xml file.
I have explored setMarginEnd(), but there are very few resources on this, seeing as it is only a year old, and I keep getting a compile error with it.
What is the best way to change android:layout_marginEnd on a condition programmatically?
FrameLayout layout = (FrameLayout) findViewById(R.id.wifi_combo);
FrameLayout.LayoutParams params = (LayoutParams) layout.getLayoutParams();
params.setMarginEnd(2);
The correct way to set the endMargin is using setMarginEnd() only, added in API 17.
http://developer.android.com/reference/android/view/ViewGroup.MarginLayoutParams.html#setMarginEnd(int)
BTW, what compile errors you are getting?
In Android project I'm trying to add databindings using CrossLight part of MvvmCross.
Bindings to standard TextView/Buttons work great. But simplest markup with Mvx.Control:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android">
<Mvx.MvxListView />
</LinearLayout>
Gives an error
"Binary XML file line #1: Error inflating class Mvx.MvxListView"
The same thing is with Mvx.Spinner.
However, when instantiating it from code in Activity.OnCreate:
_bindingContext = new MvxAndroidBindingContext(this, new LayoutInflaterProvider(LayoutInflater), _viewModel);
var view = (LinearLayout)_bindingContext.BindingInflate(Resource.Layout.Main, null);
SetContentView(view);
var spinner = new MvxSpinner(this, null, new MvxAdapter(this, _bindingContext));
view.AddView(spinner);
Everything works great (including bindings). What am I doing wrong? Is this scenario supported in general?
Or maybe I should reference anything else except nuget MvvmCross.HotTuna.CrossCore?
P.S. Haven't found any samples with custom controls and CrossLight neither on github, nor on N+1 videos
If you want to use namespace abbreviations within your non-MvvmCross application, then you'll need to add those abbreviations. This can be done using a custom binding builder or using a 'light' setup step like:
var viewResolver = Mvx.Resolve<IMvxAxmlNameViewTypeResolver>();
viewResolver.ViewNamespaceAbbreviations["Mvx"] = "Cirrious.MvvmCross.Binding.Droid.Views";
viewResolver.ViewNamespaceAbbreviations["MyApp"] = "MyApp.Controls";
When doing this within a full MvvmCross application, then you can override the Setup property ViewNamespaceAbbreviations
protected override IDictionary<string, string> ViewNamespaceAbbreviations
{
get
{
var toReturn = base.ViewNamespaceAbbreviations;
toReturn["MyApp"] = "MyApp.UI.Droid.Controls";
return toReturn;
}
}
When markup was changed to using the full namespace and layout_width and layout_height attribute was added it started to work!
<Cirrious.MvvmCross.Binding.Droid.Views.MvxSpinner
android:id="#+id/MySpinner"
android:layout_width="fill_parent"
android:layout_height="20dp"
/>
It was found when I switched to default Android inflater and it was complaining about missing layout_width in Exceptions.
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.
I created a custom SurfaceView called CaptureView and tried to add it into main.xml file:
<dev.recorder.client.CaptureView android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="#+id/capturePreview"/>
The application seems to work fine but if I switch from main.xml tab to Layout in Eclipse the text NullPointerException appears instead of layout preview.
In the Activity I binded the controls the following way:
setContentView(R.layout.main);
bindControls();
private void bindControls()
{
videoPreview = (CaptureView)findViewById(R.id.capturePreview);
txtstatus = (TextView)findViewById(R.id.txtMode);
txtTimer = (TextView)findViewById(R.id.txtTime);
}
Does anyone know how this issue could be solved?
make sure that you are initializing the view in onFinishInflate and not in the constructor.
the layout preview code might initialize your control through a different code path.