When I tap on an EditText (see the "Your answer here ..." field is the screen-cap below) which is inside a ListFragment it does not remain in focus and this makes it impossible to use. As soon as the text cursor appears on the EditText field it quickly disappears and the EditText field looses focus.
I have found few related stack posts and suspect something might be stealing the focus from the EditText. See related post here. I have also read posts on forcing focus but nothing makes sense.
I could build the view programmatically however it is much easier to use the standard ListFragment and ArrayAdapter code. The list will grow with user input and it's easier to manage with the infrastructure that Android provides.
See my screen below. On the right hand side of the screen is a ListFragment which contains custom layouts for the rows. Each row contains an EditText and a Button to submit the text.
Here is the code for the ListFragment:
public class ItemDetailFragment extends ListFragment {
/**
* The fragment argument representing the item ID that this fragment
* represents.
*/
public static final String ARG_ITEM_ID = "item_id";
private static final String STATE_ACTIVATED_POSITION = "activated_position";
/**
* The dummy content this fragment is presenting.
*/
private static DetailArrayAdapter mAdapter;
/**
* Mandatory empty constructor for the fragment manager to instantiate the
* fragment (e.g. upon screen orientation changes).
*/
public ItemDetailFragment() {
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ArrayList<DetailData> items = new ArrayList<DetailData>();
items.add(new DetailData("1"));
mAdapter = new DetailArrayAdapter(getActivity(), R.layout.detail_layout, items);
// TODO: replace with a real list adapter.
setListAdapter(mAdapter);
}
class DetailArrayAdapter extends ArrayAdapter<DetailData> {
Context context = null;
int layoutResourceId;
ArrayList<DetailData> data;
public DetailArrayAdapter(Context context, int layoutResourceId, ArrayList<DetailData> data){
super(context, layoutResourceId, data);
this.context = context;
this.layoutResourceId = layoutResourceId;
this.data = data;
}
public View getView(int position, View convertView, ViewGroup parent){
View row = convertView;
LayoutInflater inflater = ((Activity)context).getLayoutInflater();
row = inflater.inflate(layoutResourceId, parent, false);
DetailData data = this.data.get(position);
EditText text = (EditText) row.findViewById(R.id.answer);
text.setText(data.detail_number + "You answer here ...");
Button submit_button = (Button) row.findViewById(R.id.answer_submit);
submit_button.setOnClickListener(new OnClickListener(){
#Override
public void onClick(View v) {
int count = mAdapter.getCount();
int item_num = count + 1;
DetailData d = new DetailData(""+item_num);
mAdapter.add(d);
}
});
return row;
}
}
class DetailData {
public String detail_number;
public DetailData(String num){
this.detail_number = num;
}
}
}
Here is the layout code for the custom row:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal" >
<EditText
android:id="#+id/answer"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:hint="Your answer here..."
android:maxLines="1"
android:singleLine="true"/>
<Button
android:id="#+id/answer_submit"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Go" />
</LinearLayout>
Adding the following line to ItemDetailFragment.DetailArrayAdapter.getView() after the creation of the EditText object fixes the problem:
text.requestFocus();
I don't understand it, but having the EditText request focus when the view is retrieved enables that EditText (as well as the others) on the screen to be focused.
requestFocus() only works for the last edittext added, if you have several editext on the listfragment, it doesn't work
Related
I am currently working on an Application wich displays data from a SQLiteDatabase in a listview. This listview is using custom layouts. My intent was to have three different layouts, depending on what the user is doing. The first layout only displays one TextView and a Button (called row_nameonly). If the user presses this button, the layout would switch to a more detailed view (called row_viewentry). Finnaly, if the user presses the button again, the first layout is displayed once more (row_nameonly). I have tried to accomplish this, but have not found a working solution. My current version seems to change the View of the row, but the new layout is not visible. I wish to do this without having to add extra data to the database.
This is the code of the Custom CursorAdapter :
public class EntryListCursorAdapter extends CursorAdapter{
public int viewRequestPosition = -100;
public EntryListCursorAdapter(Context context, Cursor c) {
super(context, c, 0);
}
#Override
public View newView(Context context, Cursor cursor, ViewGroup parent) {
return LayoutInflater.from(context).inflate(R.layout.row_nameonly, parent, false);
}
#Override
public void bindView(View view, final Context context, final Cursor cursor) {
ViewGroup parent = (ViewGroup) view.getParent();
if(cursor.getPosition() == viewRequestPosition){
view = LayoutInflater.from(context).inflate(R.layout.row_viewentry, parent, false);
}
if(!(cursor.getPosition() == viewRequestPosition)) {
TextView nameView = (TextView) view.findViewById(R.id.txt_name);
ImageButton expandMoreButton = (ImageButton) view.findViewById(R.id.btn_expandmore);
String name = cursor.getString(cursor.getColumnIndexOrThrow("prename")) + " " +
cursor.getString(cursor.getColumnIndexOrThrow("surname"));
nameView.setText(name);
expandMoreButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
viewRequestPosition = cursor.getPosition();
notifyDataSetChanged();
}
});
}
}
TLDR: Is there a way to have the CursorAdapter change the layout of the view in wich the button was clicked, and have the new layout displayed, without adding extra data to the SQLiteDatabase ?
Thank you
(If you need any more information please ask)
In case someone is curious,
I had the Adapter take the Fragment calling it in its constructor. Then i saved the position of the ListItems i wanted to change the layout of in the fragment wich called the adapter. Then i created a new instance of the adapter, but this time the layout would changed based on the positions i saved in the fragment.
I have minidemo with three fragments. One displays two TextView and an editview directly, the next treat the same layout as rows in a list with custom adapter, the third displays a plain list with standard adapter (simple_list_item_1)
The activity uses a standard theme (Theme.AppCompat.Light.DarkActionBar), which I have not modified.
I would have expected all three blocks to be styled similarly. Instead, the second block with the custom adapter does not seem to get styled according to the light theme at all. (See screenshot at end of this post)
Ideal,ly I'd like the rows of the custom list to be styled like the first block. Alternatively, I'd like to get the style of the third block applied. However, all of the fonts and backgrounds seem to be some off color and I am not sure how to find out what is currently used as part of the theme. Therefore, fixing the middle block's style by manually assigning text colors, sizes, backgrounds, etc. is not an option.
For the whole demo please refer to MiniDemo
This is the item_row.xml layout file (The top level relative layout tag seems to be stripped by the editor. For the full layout please go to gitlab)
<TextView android:text="large"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="?android:textAppearanceLarge"
android:id="#+id/text1_field" />
<TextView android:text="large inverse"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="#+id/text2_field"
android:textAppearance="?android:attr/textAppearanceLargeInverse"
android:layout_centerHorizontal="true" />
<EditText
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:inputType="number"
android:ems="5"
android:id="#+id/edit_field"
android:layout_alignParentRight="true" />
</RelativeLayout>
The custom fragment. Again, the full version is on gitlab
public class CustomItemFragment extends ListFragment {
String[] DATA = new String[] {"foo1", "foo2", "foo3"};
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ItemAdapter adapter = new ItemAdapter(this.getActivity().getApplicationContext(), DATA);
setListAdapter(adapter);
}
}`
The adapter (gitlab):
public class ItemAdapter extends BaseAdapter {
private static final String TAG = ItemAdapter.class.getSimpleName();
private final Context mContext;
private final String[] mData;
public ItemAdapter(Context mContext, String[] data) {
this.mContext = mContext;
this.mData = data;
}
#Override
public int getCount() {
return mData.length;
}
#Override
public Object getItem(int position) {
return mData[position];
}
#Override
public long getItemId(int position) {
return position;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
LayoutInflater inflater = (LayoutInflater) LayoutInflater.from(mContext);
View rowView = inflater.inflate(R.layout.item_row, parent, false);
TextView text1View = (TextView) rowView.findViewById(R.id.text1_field);
TextView text2View = (TextView) rowView.findViewById(R.id.text2_field);
EditText editText = (EditText) rowView.findViewById(R.id.edit_field);
text1View.setText(mData[position]);
text2View.setText(mData[position]);
editText.setText(Integer.toString(position));
return rowView;
}
And then, here is a sceenshot to demo the effect.
Instead of using getApplicationContext(), use getBaseContext() or getContext()
More here : Theme/Style is not applied when inflater used with ApplicationContext
Arghh, I just found the answer, with a hint from Use android.R.layout.simple_list_item_1 with a light theme
The trick is to remove getActivityContext() and to pass in the activity object directly when creating the adapter. Sad but effective.
I am trying to add a button in the middle of my listView. Ideally The button will split the listView and it will continue afterward, but if this is not possible I will be ok with a button inside a row in the listView.
For example. My list view will have line one (image + text) , line two ( image + text) , button, and go on with the list view.
I have wrote the following code. This adds a button to a row in listView, but on the way it also adds an empty button (a button with now text) to every row in my listView. In addition the gravity setting for center is not working.
My 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="horizontal" >
<ImageView android:id="#+id/imgUserIcon"
android:layout_width="wrap_content"
android:layout_height="fill_parent"
android:layout_marginRight="10dp"
android:layout_marginTop="5dp"
android:gravity="center_vertical"
android:scaleType="fitStart" />
<Button
android:id="#+id/buttonShowHide"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center"
android:text="#string/showHide" />
<TextView android:id="#+id/txtTitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="10dp"
android:layout_marginTop="5dp"
/>
</LinearLayout>
my adapter
public class UserAdapter extends ArrayAdapter<UserAccountData> {
Context context;
int layoutResourceId;
User data[] = null;
public UserAdapter(Context context, int layoutResourceId,
UserAccountData[] data) {
super(context, layoutResourceId, data);
this.layoutResourceId = layoutResourceId;
this.context = context;
this.data = data;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View row = convertView;
UserAccountDataHolder holder = null;
if (row == null) {
LayoutInflater inflater = ((Activity) context).getLayoutInflater();
row = inflater.inflate(layoutResourceId, parent, false);
holder = new UserAccountDataHolder();
holder.imgIcon = (ImageView) row.findViewById(R.id.imgUserIcon);
holder.txtTitle = (TextView) row.findViewById(R.id.txtTitle);
holder.showHide = (Button) row.findViewById(R.id.buttonShowHide);
row.setTag(holder);
} else {
holder = (UserHolder) row.getTag();
}
User user = data[position];
holder.txtTitle.setText(user.title);
holder.imgIcon.setImageResource(user.icon);
holder.showHide.setText(user.buttonName);
return row;
}
static class UserHolder {
ImageView imgIcon;
TextView txtTitle;
Button showHide;
}
}
My Java object for the row. I have created two constructors one for the button and one for the image and text.
public class UserAccountData {
public int icon;
public String type;
public String title;
public CharSequence buttonName;
public UserAccountData(){
super();
}
// for image and text
public UserAccountData(int icon, String title, String type) {
super();
this.icon = icon;
this.title = title;
this.type = type;
}
// for button
public UserData(CharSequence buttonName, String type) {
super();
this.buttonName = buttonName;
this.type = type;
}
public void setType(String type){
this.type = type;
}
}
In my activity I am adding the following two rows to the array , that later my adapter will use to create the listView ( I am passing it an ArrayList that being changed into an Array)
user_data.add(new UserAccountData(icon, "title,"type"));
user_data.add(new UserAccountData("show Password","button"));
a) is there a way to split the listView and the middle and just add a button? and continue the same listView? Because my current solution tries to add a button to a row.
b) any ideas why I am actually also adding an empty button to the icon, title type row?
I am getting icon, title, empty button on my actual listView
Thank you very much
UPDATE:
Found two blogs
http://logc.at/2011/10/10/handling-listviews-with-multiple-row-types/
and
http://android.amberfog.com/?p=296
, but still don't have any luck. Would appreciate some more in depth help
is there a way to split the listView and the middle and just add a button? and continue the same listView? Because my current solution tries to add a button to a row.
If I understand your question you want something like:
My list view will have:
image + text
image + text
button
image + text
etc...
You can have more than one type of row layout if you override getViewTypeCount() and getItemViewType().
getViewTypeCount() should return the number of types, in this case 2.
getItemViewType(int position) will return which type the row at position is, in this case either 0 or 1.
Addition
I don't really know how to make the distinction between the image text row and the button. I tried to find a way to see if my image is null (using the 2nd constructor) , but this does not seems to work
This sounds like a good approach, but since icon is an int it will never be null, the default value for an uninitialized integer is 0. Try:
#Override
public int getItemViewType(int position) {
UserAccountData data = getItem(position);
if(data.icon == 0)
return 1;
return 0;
// The same thing in one line:
//return getItem(position).icon == 0 ? 1 : 0;
}
I have created a customized listView using the following tutorial http://www.ezzylearning.com/tutorial.aspx?tid=1763429.
My list view includes two row (each with an image and a TextView). The first row is user, and the second is password.
I am looking for a way to make the password row to masked, something like ****, and to add another row that will enable the user to set it to visible/ mask.
I found the following examples,
How to show hidden password in textview?
How to switch between hide and view password
but I have no idea how to implement this on a specific row.
my rows 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="horizontal" >
<ImageView android:id="#+id/imgUserAccountIcon"
android:layout_width="wrap_content"
android:layout_height="fill_parent"
android:layout_marginRight="10dp"
android:layout_marginTop="5dp"
android:gravity="center_vertical"
android:scaleType="fitStart" />
<TextView android:id="#+id/txtTitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="10dp"
android:layout_marginTop="5dp"
/>
</LinearLayout>
My user class
public class UserAccountData {
public int icon;
public String title;
public UserAccountData(){
super();
}
public UserAccountData(int icon, String title) {
super();
this.icon = icon;
this.title = title;
}
}
My adapter class
public class UserAccountAdapter extends ArrayAdapter<UserAccountData> {
Context context;
int layoutResourceId;
UserAccountData data[] = null;
public UserAccountAdapter(Context context, int layoutResourceId,
UserAccountData[] data) {
super(context, layoutResourceId, data);
this.layoutResourceId = layoutResourceId;
this.context = context;
this.data = data;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View row = convertView;
UserAccountDataHolder holder = null;
if (row == null) {
LayoutInflater inflater = ((Activity) context).getLayoutInflater();
row = inflater.inflate(layoutResourceId, parent, false);
holder = new UserAccountDataHolder();
holder.imgIcon = (ImageView) row.findViewById(R.id.imgUserAccountIcon);
holder.txtTitle = (TextView) row.findViewById(R.id.txtTitle);
row.setTag(holder);
} else {
holder = (UserAccountDataHolder) row.getTag();
}
UserAccountData userAccountData = data[position];
holder.txtTitle.setText(userAccountData.title);
holder.imgIcon.setImageResource(userAccountData.icon);
return row;
}
static class UserAccountDataHolder {
ImageView imgIcon;
TextView txtTitle;
}
}
and the appropriate list view snippet of the activity method
List<UserAccountData> user_data = new ArrayList<UserAccountData>();
user_data.add(new UserAccountData(R.drawable.username_icon,"userName");
user_data.add(new UserAccountData(R.drawable.password_icon,"password");
usersArray = new UserAccountData[user_data.size()];
user_data.toArray(usersArray);
UserAccountAdapter adapter = new UserAccountAdapter(this, R.layout.user_accounts_row, usersArray);
userAccountsListView = (ListView) findViewById(R.id.userAccounts);
userAccountsListView.setAdapter(adapter);
Attaching a picture of what I would like to accomplish
Before click:
after click
thanks
You could fill up a list of TextViews in your adapter while you inflated. After inflation, you'd have access to all the TextViews, and then in your onItemClickListener, when someone clicked the item in in Position 2 (in this case at least), you simply reference your list of TextViews, get the appropriate one, and change the parameters on the fly.
If you have a set, small number of rows like this a TableLayout is a better fit than a ListView. Then you could easily customize the xml of the two TextViews. Another option is to call setRawInputType to set the password on just the second TextView. You'd need to do that in the adapter's getView function based on the view's position.
Well I got around this problem by displaying the password as plain-text in an AlertDialog when the user clicks the ListView entry.
I'm having a list View with each item composed of a collection of Textviews and a CheckBox.
I'm storing the state of the checkbox in the DB and updating it from a on clickListener .It works fine for the controls that are visible.By default all the checkbox's are in the checked state.
If there are 10 items and screen can accommodate 7, then when I Uncheck the first one and scroll to the 10th item and again scroll back to the first one. The first one looses its state( it gets checked again).I checked the DB for the rows state, which is reflected correctly. But the fetch in the BindView always get me the wrong state. I'm not able to pin down where the issues is. I have attached the list adaptor along with this for review...
// List Adaptor code
public class ListAdaptor extends CursorAdapter {
private LayoutInflater mInflater;
Cursor dataCursor;
Context context;
ListView mLv;
private static final String TAG = "Delete";
public ListAdaptor(Context context, Cursor cursor, ListView lv)
{
super(context, cursor);
this.context = context;
mLv = lv;
mInflater = LayoutInflater.from(context);
}
#Override
public void bindView(View view, Context context, final Cursor cursor) {
// Get the stored tag for the view
CheckBox tmp_Chk = (CheckBox)view.findViewById(R.id.chkbox);
String selText = cursor.getString(11);
// Debug Message
int val = cursor.getPosition();
tmp_Chk.setChecked(false);
SparseBooleanArray sba = mLv.getCheckedItemPositions();
if(sba != null)
if(sba.get(cursor.getPosition()))
tmp_Chk.setChecked(true);
}
#Override
public View newView(Context context, Cursor cursor, ViewGroup parent)
{
View newView = mInflater.inflate(R.layout.listviewlyt, null);
return newView;
}
}
// Item layout
<?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">
<CheckBox
android:id="#+id/chkbox"
android:focusable="false"
android:clickable="false"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
</CheckBox>
<TextView
android:id="#+id/label"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="#+id/label"
android:textSize="18sp"
android:textColor="#000000" >
</TextView>
</LinearLayout>
// List control code in the Main Activity
lv.setOnItemClickListener(new OnItemClickListener() {
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
int lv_Pos = ListView.INVALID_POSITION;
CheckBox tmp_Chk = (CheckBox)view.findViewById(R.id.chkbox);
if (lv_Pos != ListView.INVALID_POSITION) {
if(tmp_Chk.isChecked()){
Check_Uncheck(Integer.toString(lv_Pos + 1), 1);
}
else if(!tmp_Chk.isChecked()){
Check_Uncheck(Integer.toString(lv_Pos + 1), 0);
}
}
public void Check_Uncheck(String deleteItem , int select)
{
// Initialize database
DB dbAdapters = DB.getDBAdapterInstance(TabActivity.this);
dbAdapters.openDataBase();
ContentValues cv_InitialValues = new ContentValues();
cv_InitialValues.put("Selection", select);
dbAdapters.b_UpdateRecordInDB("Items", cv_InitialValues, "_id=?", new String[] {deleteItem});
dbAdapters.close();
}
});
// List view XML Properties in the Main activity
<ListView
android:id="#+id/LV_Instore_CartTab"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:textSize="2px"
android:layout_weight="1"
android:choiceMode="multipleChoice"/>
You will want to override getView in your adapter. This function is called as views come into view when the list is scrolled. The convertView variable is the view that has just gone out of view as you scroll and is being reused. The convertView may be null so you should check if it is null and inflate a new row layout if it is null, otherwise you can use the convertView just like you would if you had inflated it. Now what is happening to you is that the view is being reused but you are not setting the state back to how you want it in the getView function. In this function you should use the position variable passed in to determine which item in your list the view is being connected with. If you store the state of the check in the list of objects you can then use the position to get the correct item out of your list. Use the object you retrieve from the list to either check or uncheck the checkbox in your row.
I am not really aware of CompundButton, but can you try the below code snippet,
tmp_Chk.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
if ( !tmp_Chk.isChecked()) {
tmp_Chk.setChecked(false);
Check_Uncheck();
} else {
Check_Uncheck();
tmp_Chk.setChecked(true);
}
}
});
if (cursor.getInt(11) == 0) {
tmp_Chk.setChecked(false);
} else {
tmp_Chk.setChecked(true);
}