Duplicating Form Layout on Click - java

I have a form that gives the user the option to add more fields to fill. The user adds more fields by clicking on a button. When the button is clicked It duplicates a RelativeLayout that houses form elements in it inside of another RelativeLayout.
This is the form that I'm trying to duplicate
<RelativeLayout
android1:id="#+id/questionTemplate"
android1:layout_width="wrap_content"
android1:layout_height="60dp"
android1:layout_alignParentLeft="true"
android1:layout_below="#+id/TextView01"
>
<Button
android1:id="#+id/Button02"
android1:layout_width="75dp"
android1:layout_height="20dp"
android1:layout_alignParentBottom="true"
android1:layout_alignParentRight="true"
android1:background="#drawable/purplebutton"
android1:text="BROWSE"
android1:textColor="#drawable/button_text_color"
android1:textSize="10sp"
/>
<EditText
android1:id="#+id/EditText02"
android1:layout_width="match_parent"
android1:layout_height="25dp"
android1:layout_above="#+id/Button02"
android1:layout_alignParentLeft="true"
android1:background="#drawable/textlines"
android1:ems="10"
android1:hint="50 WORDS OR LESS"
android1:inputType="textMultiLine"
android1:paddingLeft="5dp"
>
<requestFocus />
</EditText>
<TextView
android1:id="#+id/TextView03"
android1:layout_width="wrap_content"
android1:layout_height="wrap_content"
android1:layout_alignParentBottom="true"
android1:layout_marginRight="19dp"
android1:layout_toLeftOf="#+id/Button02"
android1:text="ADD PICTURE OR VIDEO"
android1:textSize="10sp"
/>
</RelativeLayout>
Its inside another RelativeLayout with an id of "questionsContainer". And when the user clicks on a button with an id of "makeLayoutButton" its supposed to add another instance of "questionTemplate" below another one.
Here's what I'm doing when the user clicks on the button
public void onClick(View v) {
Toast toast;
switch (v.getId()) {
case R.id.makeLayoutButton:
LayoutInflater vi = (LayoutInflater) getApplicationContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View v1 = vi.inflate(R.layout.make_question, null);
// template to be copied
RelativeLayout target = (RelativeLayout) v1.findViewById(R.id.questionTemplate);
// insert into main view
RelativeLayout insertPoint = (RelativeLayout)findViewById(R.id.questionsContainer);
insertPoint.addView(target);
break;
}
}
But it keeps crashing and the log informs me that I need to call removeView on the parent first. But when I do that it just removes all the children of the container and leaves me with one copy of the form without it duplicating. Any clues as to where I'm going wrong? I have a feeling it has something to do ids.

Of course, the output is correct. You're trying to add a View which already exists. You don't create (duplicate) new View but use the existing one by using findViewById() method.
In order to create the actual View, you should use inflate() method:
RelativeLayout target = (RelativeLayout) v1.inflate(R.id.questionTemplate, null); // if v1 is container for this R.id.questionTemplate layout

Related

Add simple navigation to Items in ListView Android studio

I was already looking for various navigation solutions but I couldn't fit any for my app.
I have Parcelable objecs which are displayed in ListView using Array Adapter. When i click on item, it goes to another activity with another layout, dedicated to display this single item with image gallery, title etc. Therefore, i would like to add simple navigation as in picture which moves to next item if exist, previous item if exist and back to List View.
Here's some code
Single Item activity
public class SingleItem extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_single_item);
Intent intent = getIntent();
Single singleItem = intent.getParcelableExtra("Single");
String id = singleItem.getId();
Integer position = singleItem.getPosition();
String title = singleItem.getTitle();
String subtitle = singleItem.getSubtitle();
String text = singleItem.getText();
int[] image = singleItem.getImage();
TextView titleTV = findViewById(R.id.singleItemTitle);
LinearLayout gallery = findViewById(R.id.gallery);
LayoutInflater inflater = LayoutInflater.from(this);
for (int i = 0; i < image.length; i++) {
View view = inflater.inflate(R.layout.single_item_gallery, gallery, false);
ImageView imageView = view.findViewById(R.id.singleItemGalleryImage);
imageView.setImageResource(image[i]);
gallery.addView(view);
}
TextView descTV = findViewById(R.id.singleItemDesc);
titleTV.setText(title);
descTV.setText(text);
}
}
Here's my adapter
val listViewAdapter = ItemListAdapter(this, R.layout.adapter_view_layout, itemList)
listView.adapter = listViewAdapter
listView.setOnItemClickListener { _: AdapterView<*>, _: View, position : Int, _: Long ->
val intent = Intent(this, SingleItem::class.java)
intent.putExtra("Single", itemList[position])
startActivity(intent)
}
Single Item layout
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
android:layout_height="match_parent"
android:orientation="vertical"
android:layout_width="match_parent"
xmlns:android="http://schemas.android.com/apk/res/android">
<TextView
android:id="#+id/singleItemTitle"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<ScrollView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_below="#+id/singleItemTitle">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<HorizontalScrollView
android:layout_width="match_parent"
android:layout_height="220dp"
android:paddingStart="10dp"
android:scrollbars="none"
android:paddingEnd="10dp">
<LinearLayout
android:id="#+id/gallery"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:orientation="horizontal"/>
</HorizontalScrollView>
<TextView
android:id="#+id/singleItemDesc"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>
</ScrollView>
</RelativeLayout>
And i would like put navigation in layout above.
List view:
Single item navigation example:
Hello and welcome to StackOverflow (SO)!
As a newcomer you may not be aware, but, on SO, you should clearly define your question. I don't see a direct question. I'm deducing that you need a set of guidance in implementing your image gallery view using Android and to be able to swipe through them using the buttons (if I'm not mistaken).
Your approach is theoretically okay. What's left to do it to get a reference to the Next/Previous buttons from your SingleItem Activity and to manually scroll the HorizontalScrollView object when you press Next/Previous buttons.
However, your code has a major flaw in the following code segment.
for (int i = 0; i < image.length; i++) {
View view = inflater.inflate(R.layout.single_item_gallery, gallery, false);
ImageView imageView = view.findViewById(R.id.singleItemGalleryImage);
imageView.setImageResource(image[i]);
gallery.addView(view);
}
This is something which you should not be doing. Instead, use a RecyclerView which will only load your image when it's required to be displayed. You're essentially creating all the image views once you visit the detail view (SingleItem).
Your current approach might work okay for a few images, but is not recommended for a lot of images as it may
Slow down and freeze the app while the images load
You'll eventually run out of memory
Once you get this implemented with the RecyclerView, give it a shot and see whether it works. If you still have trouble, please don't hesitate to ask!
Cheers!
I see two options:
Instead of putting a whole Parcelable in the intent just put some kind of ID of the object. It can be a primary key, if you store them in DB or just an array index, if it's an array in memory kept in some singleton object. Then in SingleItem activity's onCreate just find the item by ID and load it. Therefore you can open more SingleItem activities from a SingleItem activity just by passing next or previous item ID to it.
Use fragments instead. You'll be having one activity, which holds a reference to the list of items, and two fragments one for the list view one and another for a single item display. Thus in single item fragment you can call a method on parent activity once "Next" button is clicked, which will then take the next item and create a replacement single item fragment with new data.

Why do I get null pointer exception for one sceanrio but not a similar sceanrio?

The issue that I'm finding confusing is two similar situations both can be described as:-
From within an activity with a ListView I click on an Item which starts another activity to edit that item. Within the secondary activity, after editing the item and saving the changes to a database, I then need to refresh the ListView by swapping the cursor.
In one I use the following (this works without a null pointer) :-
Cursor csr = shopperdb.getProductsperAisle(aisleid);
View v = findViewById(R.id.aapts_layout);
ListView lv = (ListView) v.findViewById(R.id.aaptslv100);
ProductsPerAisleCursorAdapter adapter = (ProductsPerAisleCursorAdapter) lv.getAdapter();
adapter.swapCursor(csr);
finish();
The code above is in the activity named ProductUsageEdit which uses activity_productusage_edit.xml and has this set as the ContentView.
ListView aaptslv100 is in aapts_layout defined in activity_shop_add.xml Activity AddProductToShopActivity sets aapts_layout as the ContentView. This activity starts the ProductUsageEdit activity, when an item in the ListView aaptslv100 is clicked.
R.id.aapts_layout is the ID of the primary layout, R.id.aaptslv100 being the ID of the ListView in that layout. This code is invoked via a button's OnClickListener.
The other scenario, if using the equivalent ie getting the initial activitity's View from the layout ID and then getting the ListView's ID via that V; issues a nullpointer exception (still results in the ListView being refreshed) ie using :-
View v = findViewById(R.id.aslbc_layout);
ListView callerListView = (ListView) v.findViewById(R.id.aslbclv01);
....
Rather in the second sceanrio, to not get the null pointer exception I code :-
Cursor csr = shopperdb.getShopsAsCursor();
ListView callerListView = (ListView) findViewById(R.id.aslbclv01);
ShopsCursorAdapter calleradapter = (ShopsCursorAdapter)callerListView.getAdapter();
calleradapter.changeCursor(csr);
The code above is in ShopAddActivity, uses ListView aslbclv01 from activity ShopListByCursorActivity in layout aslbc_layout from activity_shop_list_by_cursor.xml.
(Obviously the Listview is different as are the adapters used and the cursor obtained, ps finish() is coded just a little later outside of the If clause that this code is within).
If I try using the equivalent of this in the first scenario, then I get a null pointer exception (again the ListView then gets refreshed).
What I'd like is a standard method that would suite both and other similar situations. I'd also greatly appreciate some understanding as to why I appear to need to use different methods for what is basically the same scenario.
(added)
Thinking about this a bit more, there is a difference between the two scenarios;
The first sceanrio's called secondary activity has a more complex layout. In that it is a LinearLayout with nested LinearLayouts and then the Text/EditViews.
The second scenario uses a RelativeLayout under which the views all reside.
Could this perhaps be the need to get the originating Layout ID?
However, if that is the case, it would still leave me pondering why View v = findViewById(<orginatingLayout>) sets v as null in the less complex second scenario. Perhaps findByView is viewtype sensitive?
To succesfully find a view instance by id, first you must make sure that correct layout has been set for Activity/Fragment instance. findViewById() of Activity, AFAIK, finds child view of top parent node who has same id as given id. It does not recursively find view in nested ViewGroup. ViewGroup is descendant of View and acts as a container of one or more views. LinearLayout and RelativeLayout are few example of a ViewGroup.
So if you have following layout:
res/layout/activity_main.xml
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="#dimen/activity_vertical_margin"
android:paddingLeft="#dimen/activity_horizontal_margin"
android:paddingRight="#dimen/activity_horizontal_margin"
android:paddingTop="#dimen/activity_vertical_margin"
tools:context="com.juhara.demo.intent1.MainActivity" >
<Button
android:id="#+id/btn_start"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#string/str_btn1" />
</RelativeLayout>
and inside Activity's OnCreate(), you call
setContentView(R.layout.activity_main);
then you can succesfully get Button instance by calling findViewById() of Activity
Button abutton = (Button) findViewById(R.id.btn_start);
However if you have nested layout, for example
res/layout/activity_main2.xml
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="#dimen/activity_vertical_margin"
android:paddingLeft="#dimen/activity_horizontal_margin"
android:paddingRight="#dimen/activity_horizontal_margin"
android:paddingTop="#dimen/activity_vertical_margin" >
<RelativeLayout
android:id="#+id/button_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="#dimen/activity_vertical_margin"
android:paddingLeft="#dimen/activity_horizontal_margin"
android:paddingRight="#dimen/activity_horizontal_margin"
android:paddingTop="#dimen/activity_vertical_margin" >
<Button
android:id="#+id/btn_start"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#string/str_btn1" />
</RelativeLayout>
</RelativeLayout>
then following code will make abutton set to null.
setContentView(R.layout.activity_main2);
Button abutton = (Button) findViewById(R.id.btn_start);
To properly get Button instance you have to find parent node of Button first and then use it to find view.
setContentView(R.layout.activity_main2);
View button_container = findViewById(R.id.button_container);
Button abutton = (Button) button_container.findViewById(R.id.btn_start);
After numerous attempts at getting the ListView's adapter (required for swapCursor(), changeCursor() or notifyDataSetChanged()). This included attempts at passing the object via the Intent (serialization issues). I tried onResume with a flag indicating what should cause onResume to trigger. So for now, at least, trying to retrieve another activity's layout elements is simply too difficult for my level of experience.
For the first scenario (will look at the second and others).
My final code, at least the bulk of it, which I believe can be adopted/adapted as a standard by myself, is:-
No code at all in the secondary activity that updates the data (in regard to swapping the cursror).
In the primary activity from which the second activity is called :-
public final static int RESUMESTATE_NOTHING = 0;
public final static int RESUMESTATE_PRODUCTUSAGEEDIT = 1;
public final static String[] RESUMESTATE_DESCRIPTIONS = {"Nothing","ProductUsageEdit"};
public int resume_state = RESUMESTATE_NOTHING;
public long currentshopid = -1;
public long currentaisleid = -1;
public ProductsPerAisleCursorAdapter currentppaca;
protected void onResume() {
super.onResume();
Log.i(Constants.LOG," onResume invoked in " + THIS_ACTIVITY + "- Current State=" + resume_state + "-"+RESUMESTATE_DESCRIPTIONS[resume_state]);
switch (resume_state) {
case RESUMESTATE_NOTHING: {
break;
}
case RESUMESTATE_PRODUCTUSAGEEDIT: {
Cursor csr = shopperdb.getProductsperAisle(currentaisleid);
currentppaca.swapCursor(csr);
resume_state = RESUMESTATE_NOTHING;
break;
}
}
}
With the following in the respective onClickListener that calls the secondactivity:-
resume_state = RESUMESTATE_PRODUCTUSAGEEDIT;
currentppaca = adapterpl;
currentaisleid = aisleid;
currentshopid = shopid;

How can I make my ListAdapter accept all clicks?

When developing an android application in java, I have a mainActivity which sets up a list adapter in the following way:
private void refreshDisplay()
{
adapter = new ItemAdapter(this, R.layout.listview_item_row, myList);
setListAdapter(adapter);
}
This method refreshDisplay is called when data making up the list is changed via user input and the mainactivity is returned to after completing the edit activity.
The list itself is composed of a ListView inside the MainActivity.xml file, which loads a list_item_row_layout thus:
<ListView
android:id="#android:id/list"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_alignParentTop="true" >
</ListView>
and inside the list_item_row.xml
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<TextView
android:id="#+id/TitleText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:clickable="true"/>
<TextView
android:id="#+id/DetailText"
android:layout_width="wrap_content"
android:layout_height="fill_parent"
android:clickable="true" />
</RelativeLayout>
Inside the list adapter:
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View row = convertView;
myHolder holder;
if(row == null)
{
LayoutInflater inflater = ((Activity)context).getLayoutInflater();
row = inflater.inflate(layoutResourceId, parent, false);
holder = new itemHolder();
holder.titleText = (TextView)row.findViewById(R.id.TitleText);
holder.textDetail = (TextView)row.findViewById(R.id.DetailText);
row.setTag(holder);
}
else
{
holder = (myHolder)row.getTag();
}
myItem = data.get(position);
holder.titleText.setText(myItem.getText());
holder.textDetail.setText(myItem.getDetail());
if (holder.textDetail.getVisibility()==0) {
holder.titleText.setVisibility(8);
holder.textDetail.setVisibility(0);
}
else if (holder.titleText.getVisibility()>0) {
holder.textDetail.setVisibility(8);
holder.titleText.setVisibility(0);
}
else {
holder.titleText.setVisibility(0);
holder.textDetail.setVisibility(8);
}
return row;
}
When the list is generated it displays without problem, and even runs correctly as far as accepting the first click which is supposed to expand the TitleText into the more verbose DetailText. So the DetailText starts with a Visibility of GONE and the TitleText is set to VISIBLE. If the user clicks on the listitem then it does successfully switch to the DetailText field. But the problem arises when the same item is clicked on again - the TitleText field does not replace the detailed version. I did initially think the DetailText was not clickable - but what puzzles me now is that if one of the other list items are clicked on then the original expanded one reverts back to the title and the clicked on one expands correctly! But how can I make it so that the expanded list item can be contracted into the title text without expanding another list item? Is there some problem that means a list item that is set to VISIBLE in code cannot be then set back to GONE or INVISIBLE even?
Had to use a bit of a hack in the end since was nearing the end of my usable hair to tear out haha.
Since reverting back to all todos with titles displayed was also just the way things appeared when the activity was first created - I put the following code in where the second click happened.
There is a bit of a pause though before this diplays on my HTC One, it remains to be seen when I test it on slower phones if its an acceptable solution.
Intent intentstart = new Intent(MainActivity.this, MainActivity.class);
intentstart.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(intentstart);

Null Pointer Exception Android

I am trying to make a button, count clicks and display them in a text view.
Tried everything i knew.
XML:
<LinearLayout
android:id="#+id/gpulay"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingLeft="10dp"
android:paddingRight="10dp"
android:paddingTop="20dp" >
<TextView
android:id="#+id/master_control_text_gpulay"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:text="Master Control :"
android:textAppearance="?android:attr/textAppearanceMedium" />
<Button
android:id="#+id/plus_gpulay"
android:layout_width="35dp"
android:layout_height="35dip"/>
</LinearLayout>
Here is my code:
public class Main extends Fragment {
int c=0;
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
if (container == null) {
return null;
}
ListView GPU_LAYOUT = (ListView)inflater.inflate(R.layout.gpu, container, false);
TextView text = (TextView) GPU_LAYOUT.findViewById(R.id.text1);
Button plus = (Button) GPU_LAYOUT.findViewById(R.id.butt1);
plus.setOnClickListener(new OnClickListener() { //null pointer this line, but it's from .srtText i think
public void onClick(View v) {
c++;
text.setText(c);
}
});
}
return GPU_LAYOUT;
}
App force closes when i open it, so i don't even see the main layout.
The NPE is coming from here
text.setText(c);
you are using the wrong setText() method. When you place an int in there it looks for a resource with that id. You need to give it a String. You can do this several ways. One way is to change it to
text.setText("" + c);
You could also do
text.setText(String.valueOf(c));
TextView Docs notice the different methods.
Its just wrong to create your app in onCreateView while working in an Activity.
You can still do all this in your onCreate Method.
My Guess would be that GPU_Layout is null or doesnt contain your button. Thats why button (plus) is Null and calling setOnClickListener on it throws a NullPointerException.
Also it would help if you could explain WHEN the error Occurs, during inflating/building the Activity or during actually pressing the Button
1) You need to extend your class from Fragment instead of Activity. Then your method onCreateView will work. Then set this fragment as contentView of your Activity.
2) You can't set int as text of textView or any other widget. You need to convert it into String first using String.valueOf(count);

Finding the ID of a widget in a view class

What I have is a canvas that takes up nearly all of the screen, then under that I want a row of buttons and some other widgets. So I did this.
XML Code
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:id="#+id/myLayout"
android:orientation="vertical"
android:layout_height="match_parent" >
<com.zone.manager.Tab3
android:id="#+id/tab3_display"
android:layout_width="fill_parent"
android:layout_height="620dp" />
<LinearLayout
android:layout_width="match_parent"
android:orientation="horizontal"
android:layout_height="match_parent" >
<Button
android:id="#+id/addZone"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Add Zone" />
<Button
android:id="#+id/helpZone"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Help" />
<SeekBar
android:id="#+id/seekBar1"
android:paddingTop="9dp"
android:layout_width="179dp"
android:layout_height="wrap_content" />
</LinearLayout>
Java Code
public class Tab3 extends View implements OnTouchListener, OnClickListener {
public Tab3(Context context, AttributeSet attrs) {
View parent = (View) getParent();
addZone = (Button) parent.findViewById(R.id.addZone);
addZone.setOnClickListener(this);
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
. . . draws a bunch of stuff
}
#Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.addZone:
String w = "W~20~0~0~0~0~0";
Log.d("ZoneSize", "Zone set");
MyApplication.preferences.edit().putString( "ZoneSize", w ).commit();
MyApplication.preferences.edit().putBoolean("ZoneSizeReady", true).commit();
break;
}
}
However my problem with this is that I believe the code is not reconising where the addZone is. Because when I have the addZone.setOnClickListener active my program will crash, but when I don't have it active the layout looks like how I want it. What must I do to fix this?
addZone = (Button) findViewById(R.id.addZone);
addZone will be null because com.zone.manager.Tab3 does not have any children
So it is obvious that your code will crash
So either you will give com.zone.manager.Tab children which require also to change the base class from View to ViewGroup,
or you start with com.zone.manager.Tab parent. Something like
View parent = (View) getParent ();
addZone = (Button) parent.findViewById(R.id.addZone);
i have some tips which will make you avoid such weird bugs and others:
the code of the custom view relies on an xml layout that uses the custom view .
this is bad coding.
instead, you should use LayoutInflater , use a layout xml file for the custom view for it , and then do the "findViewById" and add the clickListeners to any view inside that you wish.
i think it's also wrong to set the custom view to hold the click listener of the other views without checking which of them was clicked . either add a check , or add a different listener for each of them (which i personally prefer) .

Categories