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.
Related
I'm trying to make Image buttons act like Radio buttons. Let me explain. I have a basic layout containing an Image Button and a TextView, that I load with different images and texts.
XML layout :
<?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">
<ImageButton
android:id="#+id/button_category"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:background="#FFFFFF"/>
<TextView
android:id="#+id/text_category"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"/>
</LinearLayout>
Java method :
LinearLayout categoryLayout = view.findViewById(R.id.categories_layout);
for (int i = 0; i<categories.size(); i++) {
final String name = categories.get(i).name;
final int resource = categories.get(i).resource;
View v = getLayoutInflater().inflate(R.layout.choose_category, null);
final TextView text = v.findViewById(R.id.text_category);
text.setText(name);
ImageButton button = v.findViewById(R.id.button_category);
button.setImageResource(resource);
button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
selectedCategory = resource;
text.setTypeface(text.getTypeface(), Typeface.BOLD);
}
});
categoryLayout.addView(v);
}
Every time an image is clicked, the text is set bold to indicate which button was clicked. My problem is that I only want one button to be clickable at a time. I thought of, each time a button is clicked, reseting the appearance of all TextView, only leaving the last text that was clicked as bold. However, I don't know how to navigate through all layouts that have been generated.
I hope I was clear, thank you if you can help me !
That might not be the most efficient way to solve this problem, but it works. Every time the button is clicked, the icon selected is saved. Then, I use removeAllViews() method on the RadioGroup, and recreate the views. I just make sure the text below the previously selected icon is set bold.
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;
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
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);
I am populating a Listview (list.xml) with a textview (list_row.xml).
I need to add an additional textview to list_row.xml, which means I need to wrap them in a layout. However, I get a Textview ID error when I do this.
list_row.xml I need:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<TextView android:id="#+id/nameTV"
android:textSize="14sp"
android:textStyle="bold"
android:textColor="#FFFF00"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</LinearLayout>
Main.xml
public View GetView(int inPosition, View inConvertView, ViewGroup inParent)
{
View _row = inConvertView;
if (_row == null)
{
// Inflate Row
Log.d(TAG, "Starting XML Row Inflation ... ");
LayoutInflater inflater = (LayoutInflater)
cContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
_row = inflater.inflate(R.layout.list_row, inParent, false);
Log.d(TAG, "Successfully completed XML Row Inflation!");
}
// Get item
PropertiesAd _adProperty = GetItem(inPosition);
// Get reference to TextView - country_name
cAdName = (TextView) _row.findViewById(R.id.nameTV);
//Set name
cAdName.setText(_adProperty.toString());
return _row;
}
For the sake of testing, I haven't added the second textview yet. I'm just trying to get this to work with the layout wrapper.
Everything works fine without the LinearLayout. But again, I will need it to add a second textview. Suggestions?
Thanks
ArrayAdapter:
public class ArrayAdapterAd extends ArrayAdapter<PropertiesAd>
{
private static final String TAG = "AdArrayAdapter";
private Context cContext;
private TextView cAdName;
private List<PropertiesAd> cAdList = new ArrayList<PropertiesAd>();
public ArrayAdapterAd(Context inContext, int inTextViewResourceId, List<PropertiesAd> inObjects)
{
super(inContext, inTextViewResourceId, inObjects);
this.cContext = inContext;
this.cAdList = inObjects;
}
public int GetCount()
{
return this.cAdList.size();
}
public PropertiesAd GetItem(int inIndex)
{
return this.cAdList.get(inIndex);
}
public View GetView(int inPosition, View inConvertView, ViewGroup inParent)
{
View _row = inConvertView;
if (_row == null)
{
// Inflate Row
Log.d(TAG, "Starting XML Row Inflation ... ");
LayoutInflater inflater = (LayoutInflater)
cContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
_row = inflater.inflate(R.layout.ad_list, inParent, false);
Log.d(TAG, "Successfully completed XML Row Inflation!");
}
// Get item
PropertiesAd _adProperty = GetItem(inPosition);
// Get reference to TextView - country_name
cAdName = (TextView) _row.findViewById(R.id.ad_listTextView);
//Set country name
cAdName.setText(_adProperty.toString());
return _row;
}
}
Now I see your problem... you cannot do that extending the ArrayAdapter class. Instead, extend BaseAdapter.
Here is xml for a listview "list_row" with 2 textviews
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent" android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView android:id="#+id/text1" android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<TextView android:id="#+id/text2" android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</LinearLayout>
I'm assuming the getView() is from an ArrayAdapter subclass? If so, what does your constructor look like? Perhaps you're passing in an id reference in stead of a layout reference? This will probably resolve fine if you're whole layout is nothing more than a single TextView, but could explain why an error occurs when wrapping it in a layout.
Also, don't forget to implement the rowwrapper/viewholder pattern to improve performance and recycle views. For an example, have a look here.
By the way, you may even suffice with one of the Android built-in layouts. There a two line list item layout defined in android.R.layout.simple_list_item_2. It may not suit your needs though, as the styling on the two elements is different. A custom layout is always the most flexible in the end. :)