Implementing a table-like UI I met a really strange behaviour. After rotating the screen all rows contain the same edit text field
Here is my code:
Main activity:
public class MainActivity extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
ViewGroup layout = (ViewGroup) findViewById(R.id.table_layout);
LayoutInflater inflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
addRow(inflater, layout, "A", 0);
addRow(inflater, layout, "B", 1);
addRow(inflater, layout, "C", 2);
}
private void addRow(LayoutInflater inflater, ViewGroup layout, String title, int value) {
View view = inflater.inflate(R.layout.row, null, false);
Row row = (Row) view;
row.setTitle(title);
row.setValue(value);
layout.addView(row);
}
}
Main activity layout:
<LinearLayout xmlns:a="http://schemas.android.com/apk/res/android"
a:layout_width="match_parent"
a:layout_height="wrap_content"
a:layout_gravity="center"
a:orientation="vertical">
<LinearLayout
a:id="#+id/table_layout"
a:orientation="vertical"
a:layout_width="match_parent"
a:layout_height="wrap_content" />
</LinearLayout>
Table row:
public class Row extends LinearLayout {
public Row(Context context) {
super(context);
}
public Row(Context context, AttributeSet attrs) {
super(context, attrs);
}
public void setTitle(String title) {
final TextView titleView = (TextView) findViewById(R.id.title);
titleView.setText(title);
}
public void setValue(int i) {
final TextView valueEdit = (TextView) findViewById(R.id.value);
valueEdit.setText("" + i);
}
}
Table row layout:
<?xml version="1.0" encoding="utf-8"?>
<com.anagaf.freqhelper.Row
xmlns:a="http://schemas.android.com/apk/res/android"
a:layout_width="match_parent"
a:layout_height="match_parent"
a:orientation="horizontal">
<TextView
a:id="#+id/title"
style="#style/RangeTitle"
a:layout_marginBottom="#dimen/gap"
a:layout_height="wrap_content"/>
<EditText
a:id="#+id/value"
style="#style/ChannelEdit"/>
</com.anagaf.freqhelper.Row>
After the app starts the table looks ok:
A 0
B 1
C 2
If I change the screen orientation to landscape the table changes to:
A 2
B 2
C 2
It looks like the EditText from the last row is re-used for all rows.
If I replace value EditText with TextView - everything is ok
Any idea what's happening?
You are inflating the ViewGroup inflater.inflate(R.layout.row, null, false); as a View. Instead, inflate it as a ViewGroup then use yourviewgroup.findById(R.id.someview) to get each element you want to fill.
Even better would be to create a new layout in code, then inflate your 'template' layout and instantiate each element from there. Then you use the created layout to display the elements you just instantiated.
This way you can inflate the layout only once and pass it as a variable to each row, return a new layout and put it in the activity's layout.
Related
I am having trouble getting EditText to dynamically populate in TableView, this is only happening on non Lollipop Devices in a fragment (In particular an appcompat fragment, i have not tested in a regular framgent). When the same code is run in an activity it works fine.
Here is my fragment code on creating the row of edit texts.
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
view = inflater.inflate(R.layout.fragment_daily_field_report, container, false);
tableLayout = (TableLayout) view.findViewById(R.id.equipmentLayout);
context = getActivity().getBaseContext();
this.inflater = inflater;
activityContext = getActivity();
return view;
}
private void createRow(Equipment equipment, final int editId) {
TableRow row = new TableRow(context);
EditText equipmentName = (EditText) getActivity().getLayoutInflater().inflate(R.layout.table_text_edittext, row, false);
EditText headerQty = (EditText) getActivity().getLayoutInflater().inflate(R.layout.table_number_edittext, row, false);
EditText headerHrs = (EditText) getActivity().getLayoutInflater().inflate(R.layout.table_number_edittext, row, false);
equipmentName.setText(equipment.title);
if (equipment.Qty != 0) {
headerQty.setText(Integer.toString(equipment.Qty));
}
if (equipment.Hrs != 0) {
headerHrs.setText(Integer.toString(equipment.Hrs));
}
equipmentName.setId(editId);
this.fields.add(equipmentName);
this.fields.add(headerQty);
this.fields.add(headerHrs);
row.addView(equipmentName);
row.addView(headerQty);
row.addView(headerHrs);
tableLayout.addView(row);
rows.add(new myTableRow(equipment, equipmentName, headerHrs, headerQty));
}
And Here are the edit text Xml layouts.
<?xml version="1.0" encoding="utf-8"?>
<EditText xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="0dp"
android:layout_weight="1"
android:padding="5dp"
android:gravity="center"
android:layout_height="wrap_content"
android:inputType="text"
android:ems="10"
android:id="#+id/pumptruckQty"
android:layout_column="1"/>
I figured out what was causing the issue. When creating the TableRow to add to the TableLayout i was using the wrong context. The context used to create the Row should be the context of the LayoutInflater that is given in the onCreateView of the fragment.
Im trying to add a view to my Material dialog using setView(...), I want to have my inflated view look like this
That is the recycler view will always take up roughly 2/3 of the screen. That includes when it is empty, where it will be an empty space and when it has many lines of data, where it can become scroll able.
This is my aim. However when I try to inflate this View inside my dialog I get the following..
That screen represents an empty recyclerview taking up most of the screen.
Here is the code
//Adding to dialog
mMaterialDialog = new MaterialDialog(mContext)
.setView(new ISEQDialog(mContext))
//.setBackgroundResource(R.drawable.dublin_watchlist)
.setPositiveButton("OK", new View.OnClickListener() {
#Override
public void onClick(View v) {
mMaterialDialog.dismiss();
}
});
mMaterialDialog.show();
}
});
//View
public class ISEQDialog extends FrameLayout{
SeekBar mBuySeekBar;
TextView mStockHeading;
Context mContext;
View mView;
RecyclerView mStockDataList;
public ISEQDialog(Context context) {
super(context);
this.mContext = context;
LayoutInflater inflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
if(inflater != null){
mView = inflater.inflate(R.layout.stock_dialog, null);
}
mStockDataList = (RecyclerView) mView.findViewById(R.id.rv_stock_data_list);
//
mStockDataList.setAdapter(new ISEQDialofRecyclerViewAdapter());
LinearLayoutManager layoutManager = new LinearLayoutManager(mContext);
layoutManager.setOrientation(LinearLayoutManager.VERTICAL);
layoutManager.scrollToPosition(0);
mStockDataList.setLayoutManager(layoutManager);
//mStockDataList.addItemDecoration(new DividerItemDecoration(mContext.getDrawable(R.drawable.divider)));
addView(mView);
}
}
//RecyclerViewAdapter
public class ISEQDialofRecyclerViewAdapter extends RecyclerView.Adapter<ISEQDialofRecyclerViewAdapter.ViewHolder>{
#Override
public ISEQDialofRecyclerViewAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
return null;
}
#Override
public void onBindViewHolder(ISEQDialofRecyclerViewAdapter.ViewHolder holder, int position) {
}
#Override
public int getItemCount() {
return 0;
}
public static class ViewHolder extends RecyclerView.ViewHolder {
public ViewHolder(View itemView) {
super(itemView);
}
}
}
//XML
<?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">
<TextView
android:id="#+id/tv_stock_dialog_heading"
android:layout_weight="0.2"
android:layout_width="match_parent"
android:layout_height="0dp"
android:gravity="center"
android:textColor="#color/list_divider_pressed"
android:layout_gravity="top"
android:background="#null"
android:textSize="35dp"
android:text="Portfolio Value"
/>
<android.support.v7.widget.RecyclerView
android:id="#+id/rv_stock_data_list"
android:layout_weight="0.7"
android:layout_height="0dp"
android:divider="#drawable/list_selector"
android:dividerHeight="1dip"
android:layout_width="match_parent">
</android.support.v7.widget.RecyclerView>
<SeekBar
android:id="#+id/sb_buy_stocks"
android:layout_weight="0.1"
android:layout_width="match_parent"
android:layout_height="0dp"
android:gravity="center"
android:indeterminate="false" />
</LinearLayout>
I had the same issue trying to add the recycler view to the dialog.
When i tried troubleshooting i realized that only the constrcutor of the recycler view adapter gets called and stops. The remaining methods as getItemCount(), onCreateViewHolder() and onBindViewHolder() doesn't gets called.
So i did the following
1) i replaced the recyclerview with the linear layout.
2) referenced the linear layout as view holder in code.
3) Then i manually iterated through the list i was to pass to the recycler view and on so i inflated the single row xml file, referenced the views and set text on it.
4) I added the view to the view holder and displayed the dialog. It works
5) This operation inflates the view as we are not recycling anything so if the items to display is below 10-15 you can use this as well or else hits the performance of the app a slight.
In Activity
Dialog myTestDialog = new Dialog(getActivity());
myTestDialog.setContentView(R.layout.order_details_orders_to_deliver);
//get the layout group
ViewGroup layout = (ViewGroup) myTestDialog.findViewById(R.id.order_details_recycler_view);
List<OrderItemDetails> orderItemDetailsList = mDatabaseOperationsAdapter.getOrderDetail(ordersToDeliver.getOrderId());
for (int x = 0; x < orderItemDetailsList.size(); x++) {
OrderItemDetails orderItemDetails = orderItemDetailsList.get(x);
View view = inflater.inflate(R.layout.order_details_row, null);
TextView itemName = (TextView) view.findViewById(R.id.order_details_item_name);
TextView quantity = (TextView) view.findViewById(R.id.order_details_item_quantity);
TextView itemTotal = (TextView) view.findViewById(R.id.order_details_item_total);
itemName.setText(orderItemDetails.getProductName());
quantity.setText(String.valueOf(orderItemDetails.getProductQuantity()));
itemTotal.setText(String.valueOf(orderItemDetails.getTotalPrice()));
layout.addView(view);
}
myTestDialog.show();
Note : order_details_recycler_view is the linear layout not recycler view as i changed it to linear layout keeping the id same.
List orderItemDetailsList is the list that was to be passed to the adapter.
This problem is related to RecyclerView as i know, when it is empty it fills layout, unless you give fixed layout_height.
There is trick, which is you check list of items before you create alertDialog. If empty, create alertDialog without RecyclerView, just with warning text. Otherwise create your custom alertDialog.
I am performing some queries to a database and I'm showing the results on a listView.
This is done like this:
bd.open();
ListView listContent = (ListView)findViewById(android.R.id.list);
Cursor proc = bd.getData(3,0,query);
MyAdapt cursorAdapter = new MyAdapt(this, proc,0);
listContent.setAdapter(cursorAdapter);
and MyAdapt is something like this:
public class MyAdapt extends CursorAdapter {
private final LayoutInflater mInflater;
private int n;
public MyAdapt(Context context, Cursor c, int dbColumn) {
super(context, c);
mInflater = LayoutInflater.from(context);
mContext = context;
n = dbColumn;
}
#Override
public void bindView(View view, Context context, Cursor cursor) {
TextView fName = (TextView) view.findViewById(android.R.id.text1);
fName.setText(cursor.getString("Something returned by the cursor")));
}
#Override
public View newView(Context context, Cursor cursor, ViewGroup parent) {
if (context.getClass().getName().equals("something")) {
final View view = mInflater.inflate(R.layout.proclist, parent, false);
return view;
} else {
final View view = mInflater.inflate(android.R.layout.simple_list_item_1, parent, false);
return view;
}
}
}
When I perform a query that returns results, this is working ok and showing the results that I want.
If the query returns nothing, nothing Is shown on the listView.
In this case (where query returns nothing) I want to display the text "Somenthing not found!".
This is what I've modified:
bd.open();
ListView listContent = (ListView)findViewById(android.R.id.list);
Cursor proc = bd.getData(3,0,query);
if (proc.getCount()==0) {
LayoutInflater li = LayoutInflater.from(this);
View layout = li.inflate(android.R.layout.simple_list_item_1, null);
TextView fName = (TextView) layout.findViewById(android.R.id.text1);
fName.setText(getString(R.string.notFound));
} else {
MyAdapt cursorAdapter = new MyAdapt(this, proc,0);
listContent.setAdapter(cursorAdapter);
}
This is not working and I don't understand why.
Any suggestions?
Thanks
UPDATE
Following yours suggestions inside the if statement removed everything and put this:
View empty = findViewById(android.R.id.empty);
TextView emptyText = (TextView)empty.findViewById(android.R.id.empty);
emptyText.setText(getString(R.string.notFound));
listContent.setEmptyView(empty);
This is not working. It's giving me a NullPointerException on emptyText.setText(getString(R.string.notFound));
Wasn't this supposed to be like this?
You should used the way #aprian told like this: Add the follow segment to your layout, and do nothing in your java code.
<TextView android:id="#+id/android:empty"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="130px"
android:textSize="25px"
android:text="#+string/textview_text"/>
Update, you can also create a layout which contained a ListView named #+id/android:list and load it using setContentView
<?xml version="1.0" encoding="UTF-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:gravity="center_horizontal">
<ListView android:id="#+id/android:list"
android:layout_width="fill_parent"
android:layout_height="fill_parent" />
<TextView android:id="#+id/android:empty"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="130px"
android:textSize="25px"
android:text="#+string/textview_text"/>
</LinearLayout>
add a View with id #android:id/empty to your layout. it will shown whenever ListView is empty
i inflate by this
linear = (LinearLayout) findViewById(R.id.layout_content);
linear.setVisibility(View.VISIBLE);
LayoutInflater liInflater = (LayoutInflater)this.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
linear.addView(liInflater.inflate(R.layout.main_particularcategoryallnewslist, null));
linear.addView(liInflater.inflate(R.layout.main_particularcategoryallnewslistfv, null));
This is my listview
btnmore = (Button)findViewById(R.id.btn_more);
getListView().addFooterView(btnmore);
lv.setAdapter(adapter);
I want to inflate second time but failed.
However i can inflate firsttime which was
linear.addView(liInflater.inflate(R.layout.main_particularcategoryallnewslist, null));
What is the problem why i get this error?
java.lang.ClassCastException: android.widget.LinearLayout$LayoutParams cannot be cast to android.widget.AbsListView$LayoutParams
Try changing this:
linear.addView(liInflater.inflate(R.layout.main_particularcategoryallnewslist,
null));
To this:
linear.addView(liInflater.inflate(R.layout.main_particularcategoryallnewslist,
linear));
i think It's not because of the line you pointed out..
Did you call getLayoutParams() anywhere in your code?
Whenver you call getLayoutParams(), you show typecase to parent layoutparams.
Like, if your ListView's parent is LinearLayout then,
LinearLayout.LayoutParams params=(LinearLayout.LayoutParams) listView.getLayoutParams();
If your ListView's parent is RelativeLayout then,
RelativeLayout.LayoutParams params=(RelativeLayout.LayoutParams) listView.getLayoutParams();
I had similar situation - there was exception "java.lang.ClassCastException: android.widget.RelativeLayout$LayoutParams" on list_view.setAdapter();
I have managed to workaround this problem by using same layout for both footer and list items. The code below demonstrates how to use layout "listview_row" in footer and in items.
This is content of "listview_row.xml":
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/hs_line"
android:layout_alignParentLeft ="true"
android:layout_width="fill_parent"
android:layout_height="wrap_content">
<!-- TextView for list items -->
<TextView android:id="#+id/hs_line_textview"
android:layout_height="wrap_content"
android:layout_width="fill_parent"
android:layout_alignParentLeft="true"
/>
<!-- Button for footer -->
<Button android:id="#+id/hs_line_footer_action"
android:layout_height="wrap_content"
android:layout_width="fill_parent"
android:visibility="gone"
android:layout_alignParentLeft="true"
/>
</RelativeLayout>
Footer is initialized in such way:
View v = View.inflate(pager.Activity, R.layout.listview_row, null);
View footer = (View) v.findViewById(R.id.listview_line);
//footer doesn't use TextView, it uses Button only. So, we hide TextView.
footer.findViewById(R.id.hs_line_textview).setVisibility(View.GONE);
footer.findViewById(R.id.hs_line_footer_action).setVisibility(View.VISIBLE)
...
list_view.addFooterView(footer);
MyAdapter<Item> adapter = new MyAdapter(context, getListItems());
list_view.setAdapter(adapter);
Adapter:
public class MyAdapter<T> extends ArrayAdapter<T> {
private final ArrayList<T> _List;
private final LayoutInflater _Inflater;
public MyAdapter(Context context, ArrayList<T> srcList) {
super(context, 0, srcList);
_Inflater = LayoutInflater.from(context);
_List = srcList;
}
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder;
if (convertView == null) {
convertView = _Inflater.inflate(R.layout.listview_row, null);
holder = new ViewHolder();
//item doesn't use Button, it uses TextView only
//Button is hidden by default (see xml)
holder.TextView = (TextView) convertView.findViewById(R.id.hs_line_textview);
} else {
holder = (ViewHolder) convertView.getTag();
}
//item initialization
.....
}
It's not ideal solution - only workaround, of course.
In my Activity i have:
DrawView which is a Custom Relative Layout.
This RelativeLayout should exist of a Custom Navigationbar and a Custom GridView
The Custom NavigationBar is added here to the DrawView:
final LayoutInflater factory = getLayoutInflater();
final View textEntryView = factory.inflate(R.layout.multiplechoice, null);
com.lernapp.src.Views.NavigationBarTest navigation = (com.lernapp.src.Views.NavigationBarTest)textEntryView.findViewById(R.id.navigationbar);
RelativeLayout parent = (RelativeLayout)navigation.getParent();
parent.removeAllViews();
drawView.addView(navigation);
i wanted to do something similar with the GridView..
I dont get it, my CustomGridView is always null, why is this!?
MyCustomGridView grid = (MyCustomGridView)findViewById(R.id.gridview);
xml:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<com.lernapp.src.Views.MyCustomGridView
android:id="#+id/gridview"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:cacheColorHint="#00000000"
android:listSelector="#android:color/transparent"
android:numColumns="auto_fit"
android:columnWidth="160dp"/>
</LinearLayout>
CustomGridView:
public class MyCustomGridView extends GridView implements OnItemClickListener{
private Listener mListener;
public MyCustomGridView(Context context, AttributeSet attrs) {
super(context, attrs);
setOnItemClickListener(this);
}
public void onItemClick(AdapterView parent, View v, int position, long id) {
if (mListener != null) {
mListener.onClick(position);
}
}
public void setListener(Listener l){
mListener = l;
}
public interface Listener{
void onClick(int position);
}
}
EDIT:
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
int height = this.getWindowManager().getDefaultDisplay().getHeight();
int width = this.getWindowManager().getDefaultDisplay().getWidth();
MyCustomGridView grid = (MyCustomGridView)findViewById(R.id.gridview);
DrawView drawView = new DrawView(this, dragFieldText, targetFieldText, height, width, grid);
final LayoutInflater factory = getLayoutInflater();
final View textEntryView = factory.inflate(R.layout.multiplechoice, null);
com.lernapp.src.Views.NavigationBarTest navigation = (com.lernapp.src.Views.NavigationBarTest)textEntryView.findViewById(R.id.navigationbar);
RelativeLayout parent = (RelativeLayout)navigation.getParent();
parent.removeAllViews();
drawView.addView(navigation);
setContentView(drawView);
}
Add this line to your onCreate and all will be good:
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.the_name_of_the_layout_for_this_activity);
And to be really clear, its
setContentView(R.layout.the_name_of_the_layout_for_this_activity);
You should add to the onCreate, and it must be added BEFORE you try and do findViewById().
You have 2 options
you use findViewById. The view you're looking for MUST be "on the screen" by having used setContentView(). THere's no other way around it, that's what the function findViewById is for.
If you cannot use setContentView() because you want to programatically set this view and it is not in an XML you want to set for some reason, you have to INFLATE the view and then do your magic with it, and then you can use setContentView() as you wish. Examples of how to do this are easy, but check out the manual and some random example
As Heinrisch said you need to call setContentView before using findViewById().
Why dont you add your custom Relative Layout to your layout xml? Usually you dont need to inflate Views by yourself.
<com.lernapp.src.Views.DrawView
android:layout_width="fill_parent"
android:layout_height="fill_parent" >
<com.lernapp.src.Views.MyCustomGridView
android:id="#+id/gridview"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:cacheColorHint="#00000000"
android:columnWidth="160dp"
android:listSelector="#android:color/transparent"
android:numColumns="auto_fit" />
</com.lernapp.src.Views.DrawView>