I've got a class whose content is set to a layout which has a few buttons and a TableLayout.
The real work that makes the TableLayout is in a separate static helper class, which has a method that returns the desired table.
However, the table is not displaying. What humiliatingly simple fact am I missing?
Here is the class whose content is set to the layout:
public class TesterActivity extends Activity {
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
TableLayout table = (TableLayout) findViewById(R.id.table);
table = TableHelper.getTable_BINARY_NUMBERS(getApplicationContext(), 5, 25);
}
}
And here is the helper class that creates the Table's meat:
public class TableHelper {
public static TableLayout getTable_BINARY_NUMBERS(Context context, int numRows, int numCols) {
TableLayout table = new TableLayout(context);
table.setStretchAllColumns(true);
table.setShrinkAllColumns(true);
TableRow[] rows = new TableRow[numRows];
for (int row=0; row<numRows; row++) {
rows[row] = new TableRow(context);
for (int col=0; col<numCols-1; col++) {
TextView num = new TextView(context);
num.setText("0");
rows[row].addView(num);
}
TextView rowText = new TextView(context);
rowText.setText("Row " + (row + 1));
rowText.setTextAppearance(context, android.R.style.TextAppearance_Small);
rows[row].addView(rowText);
rows[row].setPadding(0, 50, 0, 0);
table.addView(rows[row]);
}
return table;
}
}
Instead of returning a new table layout, pass the one you get right away to your getTable_BINARY_NUMBERS() and modify it in your method instead of returning a completely new one.
The TableLayout your helper class is returning is not the same as the TableLayout that you are seeing.
when you do setContentView(R.layout.main); an instance of TableLayout is created which you assign to the variable table. The TableLayout coming from your helper class is a different instance.
Related
Background
Hi, I am new to Android and trying to get familiar with ListView. So I decide to write a simple program for user to enter quotes and display them in order. I implement a StringAdapter and call the notifyDataSetChanged every time when the user confirms.
Question
The weird thing is that the ListView would sometimes update the oldest quotes and sometimes the newer one. and I don't know the problem.
Please ignore the view data button. In this state, I have entered four quotes:
Quotes: hi - Signature:William Shakespeare
Quotes: hello - Signature:William Shakespeare
Quotes: Virtue is bold and goodness never fearful. - Signature:William Shakespeare
Quotes: Love all, trust a few, do wrong to none. - Signature:William Shakespeare
(in reverse order, meaning in time sequence, it goes 4,3,2,1)
Code
main activity
public class storage extends AppCompatActivity {
// the adapter
private StringAdapter sa;
// the edit text view
public EditText etString,etSignature;
// the list view
public ListView lv;
// the array list to capture the quotes and signature
private ArrayList<String[]> dataString = new ArrayList<String[]>();
// add the string and notify
public void addString(String[] s){
this.dataString.add(0,s);
((BaseAdapter)this.lv.getAdapter()).notifyDataSetChanged();
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_storage);
// Link the view elements
this.etString = (EditText) findViewById(R.id.etInput);
this.etSignature = (EditText) findViewById(R.id.etSignature);
this.lv = (ListView) findViewById(R.id.stringList);
Button btn_confirm = (Button) findViewById(R.id.btnConfirm),
btn_viewData = (Button) findViewById(R.id.btnViewData);
// load the adapter
this.sa = new StringAdapter(this,this.dataString);
lv.setAdapter(sa);
btn_confirm.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
storage s = (storage) v.getContext();
// get the String
String sString = s.etString.getText().toString(),
sSignature = s.etSignature.getText().toString();
System.out.println("Quotes: " + sString + "\nSignature:" + sSignature);
// verify it is not empty
if (!sString.isEmpty()&&!sSignature.isEmpty()) {
// add new string and notify
s.addString(new String[]{s.etString.getText().toString(),
s.etSignature.getText().toString()});
((StringAdapter) s.lv.getAdapter()).print_stringData();
// prompt the result
Toast.makeText(s.getBaseContext(),
"Enter Quotes from"+etSignature.getText().toString(),Toast.LENGTH_SHORT).show();
} else {
// prompt the result
Toast.makeText(s.getBaseContext(),
"Please Enter Quotes and Signatures!",Toast.LENGTH_SHORT).show();
}
}
});
}
}
StringAdapter
public class StringAdapter extends BaseAdapter {
private Context mContext;
private ArrayList<String[]> dataStrings = new ArrayList<String[]>();
public StringAdapter(Context c,ArrayList<String[]> dataStrings){this.mContext=c;this.dataStrings=dataStrings;}
public int getCount(){return this.dataStrings.size();}
public Object getItem(int position){ return this.dataStrings.get(position);}
public long getItemId(int postion){ return (long) postion;}
public void print_stringData(){
System.out.println("String Adapter Output:");
for(String [] s: this.dataStrings){
System.out.println("Quotes: "+s[0]+"\nSignature:"+s[1]);
}
}
public View getView(int position, View convertView, ViewGroup parent){
LinearLayout ll;
if(convertView == null){
// set the linear layout
ll = new LinearLayout(this.mContext);
ll.setOrientation(LinearLayout.VERTICAL);
ll.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT));
// get the data and set the text inside
String[] data = this.dataStrings.get(position);
TextView //tvNo = new TextView(this.mContext),
tvString = new TextView(this.mContext),
tvSignature = new TextView(this.mContext);
ll.addView(tvString);
ll.addView(tvSignature);
tvString.setText(data[0]);
tvString.setLayoutParams(new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT,
LinearLayout.LayoutParams.WRAP_CONTENT));
tvSignature.setText(data[1]);
tvSignature.setLayoutParams(new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT,
LinearLayout.LayoutParams.WRAP_CONTENT));
tvSignature.setGravity(Gravity.RIGHT);
}
else{
ll = (LinearLayout) convertView;
}
return ll;
}
}
Comments
Some might notice that I add the String[] always to the first element. Actually I have tried to add to the last. The weird behavior still exists.
Environment
Android SDK Version: API 23 lollipop
Emulator Version: Nexus S API 23
Yes, of course, you get that error. Why? Because ListView always re-use convertView in your getView function of Adapter.
Look at your if,else:
else{
ll = (LinearLayout) convertView;
}
return ll;
At this block, you tell the adapter reuse the convertView, but you dont re-set the data. As a result, it will show the data of the previous row.
How to resolve it? just set the data in else block as you do in if one.
P/s: you should learn how to use ViewHolder in ListView to avoid laggy in when scrolling.
I have an activity that contains a TableLayout. I'm populating this table programmaticly by data i have in a list:
what i need to do is to make every row clickable and when it's click I need to find the corresponding item in the list so i can get extra data out of it, this is my onCreate code:
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.reports_list_activity_layout);
application = (SGRaportManagerAppObj)getApplication();
reportsRepository = application.reportsRepository.getReportsRepository();
TableLayout table = (TableLayout) findViewById(R.id.tableReportsList);
table.setStretchAllColumns(true);
table.setShrinkAllColumns(true);
for (Report tempReport : reportsRepository)
{
TableRow row = new TableRow(this);
row.setClickable(true);
TextView tvName = new TextView(this);
tvName.setText(tempReport.getName());
tvName.setGravity(Gravity.CENTER_HORIZONTAL);
tvName.setTextColor(getResources().getColor(R.color.my_black));
row.addView(tvName);
TextView tvPath = new TextView(this);
tvPath.setText(tempReport.getPath());
tvPath.setGravity(Gravity.CENTER_HORIZONTAL);
tvPath.setTextColor(getResources().getColor(R.color.my_black));
row.addView(tvPath);
row.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v)
{
Map<String, String> map = new HashMap<String, String>();
map.put(Report.JSON_KEY_ID, );
}
});
table.addView(row);
}
}
so what I need here is when the row is clicked i need to find it in the list and get the ID of the report that was clicked in the table and put into the Map object to pass foreword.
does anyone know how is this can be done?
thanks.
Use a hashtable that maps the content you show in the table row (say Report ID) againsg the Report Object
So something like
Hashtable map = new Hashtable();
...
map.put(report.getId(), report);
onClick on table row, manage to get the report id and in turn look it up this hashtable you are maintaining at a class level or somewhere accessible ..
Edit(code block)
class MyTableRow extends TableRow{
Object report = null;
public MyTableRow(Context context) {
super(context);
}
public Object getReport() {
return report;
}
public void setReport(Object report) {
this.report = report;
}
}
in the onCreate() of your activity, jus use MyTableRow class instead of TableRow like
tableRow = new MyTableRow(this) and use tableRow.setMethod(reportObject) to set the Report. should work..
In one of my activities, I create a Linear Layout and some other Widgets when a bundle is received from an Intent. Currently, that Layout is overwrited each time I come back to that Activity. How can I create a new Layout each time without rewriting the code?
CODE:
public class FrontPageActivity extends Activity {
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.frontpage);
Bundle bundle = this.getIntent().getExtras();
try{
String size = bundle.getString("size");
int toppcount = bundle.getStringArrayList("toppings").toArray().length;
LinearLayout container = (LinearLayout)findViewById(R.id.container);
TextView t = new TextView(this);
TextView tprice = new TextView(this);
tprice.setGravity(Gravity.RIGHT);
LinearLayout inner = new LinearLayout(this);
LinearLayout.LayoutParams innerparams = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.FILL_PARENT,
LinearLayout.LayoutParams.WRAP_CONTENT);
inner.setLayoutParams(innerparams);
inner.setBackgroundDrawable(getResources().getDrawable(R.drawable.background));
inner.setPadding(10,10,10,10);
if(toppcount == 0){
t.setText(size+" Cheese Pizza");
}
else{
t.setText(size+" "+toppcount+" Topping Pizza");
}
tprice.setText(getPrice(size, toppcount)+"");
tprice.setLayoutParams(new LinearLayout.LayoutParams(LinearLayout.LayoutParams.FILL_PARENT,
LinearLayout.LayoutParams.FILL_PARENT));
container.addView(inner);
inner.addView(t);
inner.addView(tprice);
}
catch(NullPointerException e){
}
final Intent sender = new Intent(this.getApplicationContext(), OrderPizzaActivity.class);
Button badd = (Button)findViewById(R.id.buttonaddpizza);
badd.setOnClickListener(new View.OnClickListener() {
public void onClick(View arg0) {
startActivityForResult(sender, 0);
}
});
}
It sounds like you would need to create a data structure to hold the LinearLayouts, or provide a ViewGroup container for them to be added to each time.
Currently you are creating, modifying, and then overwriting the same LinearLayout in the try{} catch(){} block. Which I would guess to be the reason why it keeps overwriting.
As I understood you add new "options" to the "final order". Every time additional topping added you create a layout and fill it with specific data. And you want it to be aka OrderList. If what this app is about, you can have an application level variable myOrder:List. Add there toppings (topping = new Order()) and read list in FrontPageActivity.
Recommend you to have a separate layout for an order. Looping through orders fill layout with data and inflate in a container of Activity.
Idea in pseudo:
MyApplication extends Application {
private static List<Order> mOrderList = null;
public MyApplication(){
mOrderList = new ArrayList<Order>();
}
public static List<Order> getOrderList(){
return mOrderList();
}
public static void addOrder(Order order) {
mOrderList.add(order);
}
}
options activity:
MyApplication.add(order);
MyApplication.add(order);
FrontPageActivity
foreach(Order order : MyApplication.getOrderList()) {
// fill layout with order data
// inflate orderLayout into container
}
I am attempting to save and restore a view hierarchy consisting of a table of buttons. The number of table rows and buttons required in the table is not known until runtime, and are added programmatically to an inflated xml layout in my Activity's onCreate(Bundle) method. My question is: can the final table be saved and restored using Android's default view saving/restoring implementation?
An example of my current attempt is below. On the initial run, the table builds as expected. When the activity is destroyed (by rotating the device), the rebuilt view shows only an empty TableLayout with no children.
The xml file referenced in setContentView(int) includes, among other things, the empty TableLayout that the buttons are added to.
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Setup this activity's view.
setContentView(R.layout.game_board);
TableLayout table = (TableLayout) findViewById(R.id.table);
// If this is the first time building this view, programmatically
// add table rows and buttons.
if (savedInstanceState == null) {
int gridSize = 5;
// Create the table elements and add to the table.
int uniqueId = 1;
for (int i = 0; i < gridSize; i++) {
// Create table rows.
TableRow row = new TableRow(this);
row.setId(uniqueId++);
for (int j = 0; j < gridSize; j++) {
// Create buttons.
Button button = new Button(this);
button.setId(uniqueId++);
row.addView(button);
}
// Add row to the table.
table.addView(row);
}
}
}
My understanding is that Android saves the state of views as long as they have an ID assigned to them, and restores the views when the activity is recreated, but right now it seems to reinflate the xml layout and nothing more. When debugging the code, I can confirm that onSaveInstanceState() is called on each Button in the table, but onRestoreInstanceState(Parcelable) is not.
After searching through the source code for Activity, View and ViewGroup, I learned that the programmatically added views must be programmatically added and assigned the same ID each time onCreate(Bundle) is called. This is true regardless of whether it is creating the view for the first time, or recreating the view after destroying the activity. The saved instance state of the programmatically added views is then restored during the call to Activity.onRestoreInstanceState(Bundle). The easiest answer to the code above is simply to remove the check for savedInstanceState == null.
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Setup this activity's view.
setContentView(R.layout.game_board);
TableLayout table = (TableLayout) findViewById(R.id.table);
int gridSize = 5;
// Create the table elements and add to the table.
int uniqueId = 1;
for (int i = 0; i < gridSize; i++) {
// Create table rows.
TableRow row = new TableRow(this);
row.setId(uniqueId++);
for (int j = 0; j < gridSize; j++) {
// Create buttons.
Button button = new Button(this);
button.setId(uniqueId++);
row.addView(button);
}
// Add row to the table.
table.addView(row);
}
}
If it works with Views with an ID, why not give each button an ID in the table when you are creating them? See if that works.
How can I assign an ID to a view programmatically?
This line recreates an empty layout:
setContentView(R.layout.game_board);
Besides, you assign equal id's to rows and buttons. You should have used one counter for both the rows and buttons:
int idCounter = 1;
for (int i = 0; i < gridSize; i++) {
TableRow row = new TableRow(this);
row.setId(idCounter++);
for (int j = 0; j < gridSize; j++) {
Button button = new Button(this);
button.setId(idCounter++);
row.addView(button);
}
...
}
I've specified a class, based on another one in an existing Android project. The addRow() method is supposed to dynamically add rows to a table.
When creating a new TextView to add to my row and also when creating that row, I'm supposed to specify the "context". The current way, trying "getApplicationContext()" throws a NullPointerException.
So where am I supposed to get that context from?
public class DistanceTableView extends ContactListActivity
{
public void addRow(LocationMessage locationMsg){
View messageView = theInflater.inflate(R.layout.homepage, null);
TableLayout table = (TableLayout)messageView.findViewById(R.id.distanceTable);
TextView senderNameTextView = new TextView(getApplicationContext());
senderNameTextView.setText(locationMsg.getSenderName());
TableRow tr = new TableRow(getApplicationContext());
tr.addView(distanceTextView);
table.addView(tr);
rows.addFirst(messageView);
}
}
The class that my view is extending:
public class ContactListActivity extends MapActivity implements
ConnectionListener {}
I guess you have to pass the context to the constructor of your class.
Try this instead:
TableRow tr = new TableRow(this);