How to Switch Views at Runtime? - android

I have the following code in my accounting application:
// switch View to the Customer layout, widget id's are the same on both layouts
private void hideExpenseView() {
setContentView(R.layout.customer_invoices);
}
// switch View to the Supplier layout
private void hideIncomeView() {
setContentView(R.layout.supplier_invoices);
}
The above does not work, as when you switch the ContentView, you lose all variable mappings. You have to map variables after you setContentView() unfortunately.
If this worked, this would be a beautifully simple solution for my app. See, I've named the widgets in both xml layouts the same ids. Instead of hiding elements of one xml layout based on different states, I switch the entire View to the appropriate layout - whether entering a Customer sales invoice, or a Supplier expense invoice.
By switching Views, I would have basically 6 lines of code taking care of the UI transition, very simple.
I hope this is still possible in another capacity, can someone please push me in the right direction?

Check out ViewSwitcher : see http://developer.android.com/reference/android/widget/ViewSwitcher.html
That, or base your activities layout in a framelayout that includes supplier_invoices.xml and customer_invoices.xml. Then your homegrown hide-n-show will be g2g. Tho, you might need to change the ids still.

You can wrap your views in two LinearLayouts, one for R.layout.customer_invoices and another for R.layout.supplier_invoices.
You need to implement your own findViewById.
private static final int LAYOUT_EXPENSE = 1;
private static final int LAYOUT_INCOME = 2;
private int currentLayout = LAYOUT_EXPENSE;
private LinearLayout expenseContainer, incomeContainer;
// switch View to the Customer layout, widget id's are the same on both layouts
private void hideExpenseView() {
switchLayout(LAYOUT_INCOME);
}
// switch View to the Supplier layout
private void hideIncomeView() {
switchLayout(LAYOUT_EXPENSE);
}
private void switchLayout(int layout) {
currentLayout = layout;
if (layout == LAYOUT_EXPENSE) {
expenseContainer.setVisibility(VISIBLE);
incomeContainer.setVisibility(GONE);
} else {
expenseContainer.setVisibility(GONE);
incomeContainer.setVisibility(VISIBLE);
}
}
public View findViewById(int id) {
if (layout == LAYOUT_EXPENSE) return expenseContainer.findViewById(id);
else return incomeContainer.findViewById(id);
}
I think you got my idea.

Do like this
main.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" >
<LinearLayout
android:id="#+id/customer_invoices"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical" >
<!-- put customer_invoices related tools like TextView, Button, ImageView here -->
</LinearLayout>
<LinearLayout
android:id="#+id/supplier_invoices"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical" >
<!-- put supplier_invoices related tools like TextView, Button, ImageView here -->
</LinearLayout>
</LinearLayout>
Java code:
public class TestActivity extends Activity {
View supplier_invoices,customer_invoices;
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
supplier_invoices = findViewById(R.id.supplier_invoices);
customer_invoices = findViewById(R.id.customer_invoices);
}
// switch View to the Customer layout, widget id's are the same on both layouts
private void hideExpenseView() {
setContentView(R.layout.customer_invoices);
customer_invoices.setVisibility(View.VISIBLE);
supplier_invoices.setVisibility(View.GONE);
}
// switch View to the Supplier layout
private void hideIncomeView() {
supplier_invoices.setVisibility(View.VISIBLE);
customer_invoices.setVisibility(View.GONE);
}
}

Related

Two way databinding with collection on Xamarin.Android/Android

I have a UI with some EditTexts in it and this set of edittexts can repeat number of times (not too much but 3 - 10 times max) based on the number of items in the list.
User can edit/modify/delete the item or edit the value of the edit texts. Currently I am doing this manually with "AddView/RemoveView", manually handling the states etc, however it is a lot of work as I have many scenarios like this.
We have a web app with the very same functionalities and we are using AngularJS to deal with all these, which, as you know is amazingly easy.
is there any closer way to bind the axml/xml view with a collection (may be an Observable collection and at least from the code behind) that will take care of collection changes as well as the individual field changes without me doing all this manually. In some scenarios I have to display images as well.
Also, I tried using a ListView, however it doesn't work as I would expect it to work.
is there any closer way to bind the axml/xml view with a collection (may be an Observable collection and at least from the code behind) that will take care of collection changes as well as the individual field changes without me doing all this manually.
The answer is no, there isn't. Android's views have to be bound to certain context/activity when they are created. They can't be isolated, so add/remove the EditTexts have to be implemented by yourself.
Currently, the closest way to your requirement is to create an ObservableCollection and listen for the CollectionChanged event and when CollectionChanged add/remove the view in your container:
[Activity(Label = "Demo", MainLauncher = true)]
public class MainActivity : Activity
{
Button btnAdd;
ObservableCollection<View> oc;
LinearLayout container;
protected override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
// Set our view from the "main" layout resource
SetContentView(Resource.Layout.Main);
btnAdd = FindViewById<Button>(Resource.Id.btnAdd);
btnAdd.Click += BtnAdd_Click;
GenerateET(Resource.Id.container, this, 3);
}
private void BtnAdd_Click(object sender, System.EventArgs e)
{
EditText et = new EditText(this);
et.Text = "test";
et.LayoutParameters = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MatchParent, ViewGroup.LayoutParams.WrapContent);
oc.Add(et);
}
public void GenerateET(int resId, Activity activity,int num)
{
//create an observable collection
oc = new ObservableCollection<View>();
container = activity.FindViewById<LinearLayout>(resId);
for (int i = 0; i < num; i++)
{
EditText et = new EditText(activity);
et.Text = "test";
et.LayoutParameters = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MatchParent, ViewGroup.LayoutParams.WrapContent);
container.AddView(et);
oc.Add(et);
}
oc.CollectionChanged += Oc_CollectionChanged;
}
private void Oc_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
if (e.Action == System.Collections.Specialized.NotifyCollectionChangedAction.Add)
{
for (int i = 0; i < e.NewItems.Count; i++)
{
//add the view manually
container.AddView((View)e.NewItems[i]);
}
}
}
}
Main.axml:
<?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">
<LinearLayout
android:orientation="vertical"
android:id="#+id/container"
android:layout_width="match_parent"
android:layout_height="wrap_content">
</LinearLayout>
<Button
android:id="#+id/btnAdd"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Add EditText"/>
</LinearLayout>

Multiple EditText in ListView, tap to focus on one EditText and focus jumps to the first

I've got EditTexts in my rows in a ListView. When I tap on one of the EditTexts the soft keyboard appears and the focus jumps to the first EditText in the list instead of staying in the field where I tapped.
Here is a video of it:
https://youtu.be/ZwuFrX-WWBo
I created a completely stripped down app to demonstrate the problem. The full code is here: https://pastebin.com/YT8rxqKa
I'm not doing anything to alter the focus in my code:
#Override
public View getView(int position, View convertView, ViewGroup parent) {
if (convertView == null) {
convertView = layoutInflater.inflate(R.layout.cell_textfield, parent, false);
}
TextView label = (TextView) convertView.findViewById(R.id.textview1);
EditText textfield = (EditText) convertView.findViewById(R.id.textview2);
String text = String.format("Row %d", position);
label.setText(text);
textfield.setText(text);
return convertView;
}
I found another post on StackOverflow giving a workaround for this dumb Android behavior, which involves putting an OnFocusChangedListener on all of the textfields so they can retake focus if it's taken from them improperly.
That worked to regain focus, but then I discovered that when a textfield retakes focus the cursor ends up at the start of the text instead of end, which is unnatural and annoying to my users.
Here is a video of that:
https://youtu.be/A35wLqbuIac
Here's the code for that OnFocusChangeListener. It works to fight the stupid Android behavior of moving focus, but the cursor is misplaced after it regains focus.
View.OnFocusChangeListener onFocusChangeListener = new View.OnFocusChangeListener() {
#Override
public void onFocusChange(View view, boolean hasFocus) {
long t = System.currentTimeMillis();
long delta = t - focusTime;
if (hasFocus) { // gained focus
if (delta > minDeltaForReFocus) {
focusTime = t;
focusTarget = view;
}
}
else { // lost focus
if (delta <= minDeltaForReFocus && view == focusTarget) {
focusTarget.post(new Runnable() { // reset focus to target
public void run() {
Log.d("BA", "requesting focus");
focusTarget.requestFocus();
}
});
}
}
}
};
I hate having to put a bandaid on a bandaid on a bandaid to try to get Android to just behave as it would naturally be expected to behave, but I'll take what I can get.
1) Is there something I can do to fix this problem at the source and not have to have the OnFocusChangeListener at all?
2) If (1) isn't possible, then how can I make sure that when I force focus back to the correct field that I make sure the cursor is placed at the end? I tried using setSelection() right after requestFocus() but since the textfield wasn't yet focused the selection is ignored.
Here was my "solution." In short: ListViews are stupid and will always be a total nightmare when EditTexts are involved, so I changed my Fragment/Adapter code to be able to adapt to either a ListView layout or a ScrollView layout. It only works if you have a small number of rows, because the scrollview implementation isn't able to take advantage of lazy-loading and view recycling. Thankfully, any situation wherein I want EditTexts in a ListView, I rarely have more than 20 rows or so.
When inflating my view in my BaseListFragment, I get my layout id via a method that relies on a hasTextFields() method:
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(getLayoutId(), container, false);
return view;
}
public boolean hasTextfields() {
return false;
}
public int getLayoutId() {
if (hasTextfields()) {
return R.layout.scrollfragment;
} else {
return R.layout.listfragment;
}
}
In my various subclasses of my BaseListFragment, if I need to have an EditText in one of my fields, I just override the hasTextFields() method to return true and then my fragment/adapter switchs over to using the basic scrollview implementation.
From there, it's a matter of making sure that the Adapter handles the standard ListView actions for both the ListView and the ScrollView scenarios. Like this:
public void notifyDataSetChanged() {
// If scrollContainer is not null, that means we're in a ScrollView setup
if (this.scrollContainer != null) {
// intentionally not calling super
this.scrollContainer.removeAllViews();
this.setupRows();
} else {
// use the real ListView
super.notifyDataSetChanged();
}
}
public void setupRows() {
for (int i = 0; i < this.getCount(); i++) {
View view = this.getView(i, null, this.scrollContainer);
view.setOnClickListener(myItemClickListener);
this.scrollContainer.addView(view);
}
}
One issue that the click listener presented is that a ListView wants an AdapterView.OnItemClickListener, but arbitrary Views inside a ScrollView want a simple View.OnClickListener. So, I made my ItemClickListener also implement View.OnClickListener and then just dispatched the OnClick to the OnItemClick method:
public class MyItemClickListener implements AdapterView.OnItemClickListener, View.OnClickListener {
#Override
public void onClick(View v) {
// You can either have your Adapter set the tag on the View to be its position
// or you could have your click listener use v.getParent() and iterate through
// the children to find the position. I find its faster and easier to have my
// adapter set the Tag on the view.
int position = v.getTag();
this.onItemClick(null, v, config.getPosition(), 0);
}
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
// ...
}
}
Then in MyEditTextListFragment, I create the adapter like this:
listener = createClickListener();
adapter = createListAdapter();
if (scrollContainer != null) {
adapter.setScrollContainer(scrollContainer);
adapter.setMenuItemClickListener(listener);
adapter.setupRows();
} else {
getListView().setOnItemClickListener(listener);
getListView().setAdapter(adapter);
}
Here is my scrollfragment.xml for reference:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#fff"
android:clickable="true"
>
<!--
The following LinearLayout as a focus catcher that won't cause the keyboard to
show without it, the virtual keyboard shows up immediately/always which means we
never get to the enjoy the full size of our screen while scrolling, and
that sucks.
-->
<LinearLayout
android:focusable="true"
android:focusableInTouchMode="true"
android:layout_width="0px"
android:layout_height="0px"/>
<!--
This ListView is still included in the layout but set to visibility=gone. List
fragments require a standard ListView in the layout, so this gets us past that
check and allows us to use the same adapter code in both listview and scrollview
situations.
-->
<ListView android:id="#id/android:list"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1"
android:drawSelectorOnTop="false"
android:background="#null"
android:layout_alignParentTop="true"
android:descendantFocusability="afterDescendants"
android:visibility="gone"
/>
<!--
This scrollview will act as our fake listview so that we don't have to deal with
all the stupid crap that comes along with having EditTexts inside a ListView.
-->
<ScrollView
android:id="#+id/scrollView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:descendantFocusability="afterDescendants"
>
<LinearLayout
android:id="#+id/scrollContainer"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
>
</LinearLayout>
</ScrollView>
</RelativeLayout>
Try this once, it worked for me:
public void setCursorPosition() {
focusTarget.requestFocus();
focusTarget.setCursorVisible(true);
other.setCursorVisible(false);
} else {
other.setCursorVisible(true);
focusTarget.setCursorVisible(false);
}
}

Find out which layout file is currently being displayed on the screen

I currently have an AppCompatActivity and I want to be able to switch its layout using one of the menu buttons I have set up.
I am able to do that currently using setContentView, however in order to then switch back to the original View displayed, I need to know which one is currently displayed.
How do I go about getting the current ID of the layout file being displayed?
This is what I have currently, the logic is okay but the code doesn't seem to work:
View currentLayout = findViewById(android.R.id.content);
int currentLayoutID = currentLayout.getId();
if (currentLayoutID == R.layout.two) {
setContentView(R.layout.one);
} else if (currentLayoutID == R.layout.one) {
setContentView(R.layout.two);
}
You can use findViewById to find a particular view that exists only in the one which is currently. If findViewById doesn't return null, that means you were viewing that particular layout.
You compare the Id of a view with the layout name:
int currentLayoutID = currentLayout.getId();
if (currentLayoutID == R.layout.two) {
I would introduce a simple class attribute which store the current chosen layout:
private static final int CUR_LAYOUT_ONE = 1;
private static final int CUR_LAYOUT_TWO = 2;
private int currentLayoutID;
// ....
if (currentLayoutID == CUR_LAYOUT_TWO) {
setContentView(R.layout.one);
currentLayoutID = CUR_LAYOUT_ONE;
} else if (currentLayoutID == R.layout.one) {
setContentView(R.layout.two);
currentLayoutID = CUR_LAYOUT_TWO;
}
Maybe you need some additional onSaveInstanceState()-behaviour. Depends on your use case.
if (currentLayoutID == R.layout.two) {
setContentView(R.layout.one);
} else if (currentLayoutID == R.layout.one) {
setContentView(R.layout.two);
}
You are comparing the id to the R.layout. Those are two different entries in the R file. You need to compare the the actual Ids you've given your views. Usually you set them in the xml file.
For instance R.id.layout1
Maybe you should consider using ViewSwitcher. This will be a lot faster then setContentView
http://developer.android.com/reference/android/widget/ViewSwitcher.html
You just define ViewSwitcher as the parent of your views (you can switch between 2 views only) in xml like this:
<ViewSwitcher
android:id="#+id/viewSwitcher"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
and you programatically switch between views like this:
switcher.setDisplayedChild(1);

Dynamic user interface creation problem

How to Create dynamic user interface with events...
Hi Friends i want to create a user interface. in which i will have some buttons and labels
but the number of buttons and lables will be changing time to time depending upon the data retrieved from the server...
Can any one please guide me to do solve this issue... with usefuls links and guidence..
This uses two linear layouts to achieve a grid. The higher level LinearLayout is defined in linearlayout.xml, and holds element views vertically. Within each of these vertical views, a Row (custom LinearLayout) is instanciated, and in doing so, specifies the event handler(s). This LinearLayout holds its elements horizontally.
public class YourActivity extends Activity {
#Override
public void onCreate(Bundle bundle){
super.onCreate(bundle);
setContentView(R.layout.linearlayout);
ViewGroup main = (ViewGroup) findViewById(R.id.linearlayout);
main.addView(new Row(this));
main.addView(new Row(this));
}
private class Row extends LinearLayout {
public Row(Context context) {
super(context);
TextView text = new TextView(context);
Button button = new Button(context);
text.setText("Text");
button.setText("Button");
button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Toast.makeText(v.getContext(), "Button Clicked", Toast.LENGTH_SHORT).show();
}
});
this.addView(text);
this.addView(button);
}
}
}
linearlayout.xml:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/linearlayout"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
</LinearLayout>
Is this what you were looking for?

Android: swapping two overlapping views

Can anyone tell me what's wrong with this implementation? All I want to do here is have two overlapping views that swap places when you tap the screen. Unless I'm just using it wrong, View.bringToFront() does nothing?
Below is all the code in my app. Note that I added padding to the 'backView' just to make sure the two were actually overlapping. Indeed I could see both on the screen. While tapping the top view does indeed trigger the onClick method, nothing visibly changes in response to the calls to bringToFront.
public class MainActivity extends Activity implements OnClickListener {
private ImageView frontView;
private ImageView backView;
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
frontView = (ImageView) findViewById(com.example.R.id.FrontView);
backView = (ImageView) findViewById(com.example.R.id.BackView);
frontView.setOnClickListener(this);
backView.setOnClickListener(this);
backView.setPadding(10,0,0,0);
}
private boolean flag;
public void onClick(View v) {
if (!flag) {
backView.bringToFront();
}
else {
frontView.bringToFront();
}
flag = !flag;
}
}
and the corresponding layout:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
<ImageView
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:id="#+id/FrontView"
android:src="#drawable/front"
/>
<ImageView
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:id="#+id/BackView"
android:src="#drawable/back"
/>
</RelativeLayout>
Maybe it's the layout I'm using? I'm not sure... I've tried FrameLayout and LinearLayout as well.
I would try swapping content views instead of ImageViews.
Put each imageView in a different layout and then it is easy:
public void onClick(View v) {
if (!flag) {
setContentView(R.layout.main_front);
frontView = (ImageView) findViewById(com.example.R.id.FrontView);
frontView.setOnClickListener(this);
}
else {
setContentView(R.layout.main_back);
backView = (ImageView) findViewById(com.example.R.id.BackView);
backView.setOnClickListener(this);
}
flag = !flag;
}
There are a couple of Components that you can use that do this for you.
ViewAnimator, ViewFlipper and ViewSwitcher. You can set the animations you require etc and they hand the rest.
here's one example.
http://www.androidpeople.com/android-viewflipper-example/
Given your example, do you have to call invalidate() on the parent after you've called bringToFront() ?

Categories

Resources