im having an issue when i try to make a linearlayout with multiple listviews. originally when i added multiple custom linearlayouts containing a textview and a listview i could not scroll down the page so i found out adding a scrollview would fix this, which it did but for some reason it then cropped the linearlayout view so i could only see the top part of each list.
heres the code for the different parts:
Main layout
<?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:weightSum="1">
<TextView
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceLarge"
android:id="#+id/whatsNewTitle"
android:text="Whats New?"
android:layout_width="wrap_content"
android:layout_gravity="center_horizontal"
android:textSize="30dip">
</TextView>
<Button android:layout_height="wrap_content"
android:id="#+id/home"
android:text="Home"
android:layout_weight="0.07"
android:layout_width="126dp">
</Button>
<ScrollView
android:id="#+id/sv1"
android:layout_width="fill_parent"
android:fillViewport="true"
android:layout_height="630dp">
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:id="#+id/whats_new_content"
android:layout_height="fill_parent">
</LinearLayout>
</ScrollView>
</LinearLayout>
layout im inflating and adding multiple times into main layout:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:weightSum="1">
<TextView
android:text="TextView"
android:textAppearance="?android:attr/textAppearanceMedium"
android:id="#+id/new_date"
android:layout_width="match_parent"
android:layout_height="wrap_content" android:textSize="25dip">
</TextView>
<ListView
android:id="#+id/new_files"
android:layout_width="match_parent"
android:layout_height="wrap_content" android:background="#000000">
</ListView>
</LinearLayout>
the main activity that creates the multiple listviews and displays them:
public class WhatsNew extends Activity{
private Button homeBtn;
public void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.whats_new_main);
CreateLastModDateList();
homeBtn = (Button) findViewById(R.id.home);
homeBtn.setOnClickListener(new OnClickListener() {
public void onClick(View v){
Intent myIntent = new Intent(getApplication().getApplicationContext(),
Main.class);
startActivityForResult(myIntent, 0);
}
});
}
private ArrayList<File> GenerateNewFileDates(){
Files.clearFilesList();
Files.buildFilesList(Files.getRoot());
return Files.getAllFiles();
}
private void CreateLastModDateList(){
ArrayList<File> files = GenerateNewFileDates();
ArrayList<String> allDates = new ArrayList<String>();
ArrayList<String> dates = new ArrayList<String>();
Context c = this.getApplicationContext();
LinearLayout l = (LinearLayout) findViewById(R.id.whats_new_content);
/** Builds a list of all file dates **/
for(File file : files){
Date lastModifiedDate = new Date(file.lastModified());
allDates.add(lastModifiedDate.toString());
}
/** For each date, check if we have already put that date into the dates array
if we have then we want to ignore it, otherwise add it to the dates array. **/
for(String date : allDates){
if (exists(dates,date) == false){
dates.add(date);
}
}
for(String date : dates){
ArrayList<String> test = new ArrayList<String>();
test = generateFileList(date, files);
WhatsNewLayout whatsNew = new WhatsNewLayout(c,test, date);
l.addView(whatsNew);
}
}
public ArrayList<String> generateFileList(String date, ArrayList<File> mFiles){
ArrayList<String> filesFromDate = new ArrayList<String>();
for(File file : mFiles){
Date lastModifiedDate = new Date(file.lastModified());
boolean x = lastModifiedDate.toString().equals(date);
if (x == true){
filesFromDate.add(file.getName());
}
}
return filesFromDate;
}
public boolean exists(ArrayList<String> list, String compare){
boolean result = false;
if(list.contains(compare)){
result = true;
}
return result;
}
}
The custom linearlayout i inflate:
public class WhatsNewLayout extends LinearLayout {
private LayoutInflater mInflater;
public WhatsNewLayout(Context context, ArrayList<String> files, String date) {
super(context);
mInflater = LayoutInflater.from(context);
View convertView = new View(context);
/** initialize variables **/
DataHolder holder;
Context c = this.getContext();
ArrayAdapter<String> list = new ArrayAdapter<String>(c,
android.R.layout.simple_list_item_1, files);
convertView = mInflater.inflate(R.layout.whats_new_item, null);
holder = new DataHolder();
holder.modDate = (TextView) convertView.findViewById(R.id.new_date);
holder.FileList = (ListView) convertView.findViewById(R.id.new_files);
convertView.setTag(holder);
holder.modDate.setText(date);
holder.FileList.setAdapter(list);
this.addView(convertView);
}
}
Sorry about the large mass of code, but i didnt want to leave any details out in case it is important, hopefully this is everything, im sure its something really fundermental with my call of the layout but i just cannot see what is casuing the custom layout to be cut.
if any more details are required please let me know. =)
Put your listViews in a vertical scroll. You can have scrollable listView inside of a vertical scroll by the following trick. use the following code and enjoy!
private int listViewTouchAction;
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//...
setListViewScrollable(myListView1);
setListViewScrollable(myListView2);
}
private void setListViewScrollable(final ListView list) {
list.setOnTouchListener(new OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
listViewTouchAction = event.getAction();
if (listViewTouchAction == MotionEvent.ACTION_MOVE)
{
list.scrollBy(0, 1);
}
return false;
}
});
list.setOnScrollListener(new OnScrollListener() {
#Override
public void onScrollStateChanged(AbsListView view,
int scrollState) {
}
#Override
public void onScroll(AbsListView view, int firstVisibleItem,
int visibleItemCount, int totalItemCount) {
if (listViewTouchAction == MotionEvent.ACTION_MOVE)
{
list.scrollBy(0, -1);
}
}
});
}
listViewTouchAction is a global integer value.
It is not possible to have a ListView inside ScrollView (since the ListView is also a scrollable component) this is why your ListView is cropped to the screen.
If you need to display several List on screen to let user select something, you may use Spinner instead of List
Related
I am writing the basic task management application. I am using base adapter to work with ListView. I wonder how I can remove (a few) checked values from the list? And how I can remove only one value with long click?
if this posible with my adapter? I tried a few options but nothing worked.
Here is my code.
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent"
>
<Button
android:id="#+id/btn1"
android:layout_width="50dp"
android:layout_height="40dp"
android:layout_alignParentRight="true"
android:text="\u2713"
android:background="#drawable/add_btn"
android:onClick="knopka2"/>
<EditText
android:id="#+id/input"
android:layout_width="wrap_content"
android:layout_height="50dp"
android:layout_alignParentLeft="true"
android:layout_toLeftOf="#id/btn1"
android:hint="put your task here"/>
<ListView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="#+id/listView"
android:divider="#drawable/deriver"
android:layout_below="#+id/input"
android:layout_centerHorizontal="true"
android:choiceMode="multipleChoice" />
<TextView
android:id="#+id/ttl"
android:layout_below="#id/listView"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<Button
android:id="#+id/tryClear"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="#id/ttl"
android:onClick="tryClear"
/>
</RelativeLayout>
This is my Activity:
public class Trird extends AppCompatActivity {
Button btn;
EditText input4;
TextView ttl;
ListView list;
public static ArrayList<String> taskList = new ArrayList<String>();
SparseBooleanArray sparseBooleanArray;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.third);
input4 = (EditText) findViewById(R.id.input);
btn = (Button) findViewById(R.id.btn1);
list = (ListView) findViewById(R.id.listView);
ttl = (TextView)findViewById(R.id.ttl);
}
public String[] putToArray() {
String ag = input4.getText().toString().trim();
if (ag.length() != 0) {
taskList.add(ag);
input4.setText("");
}
String[] abc = taskList.toArray(new String[taskList.size()]);
return abc;
}
public void knopka2(View v) {
final String[] ListViewItems = putToArray(); //
list = (ListView) findViewById(R.id.listView);
list.setAdapter(new MyAdapter(ListViewItems, this));
list.setOnItemClickListener(new AdapterView.OnItemClickListener() {
public void onItemClick(AdapterView<?> parent, View tv, int i, long id) {
///tv.setBackgroundColor(Color.YELLOW);
///ttl.setText("selected: " + list.getAdapter().getItem(i));
sparseBooleanArray = list.getCheckedItemPositions();
String ValueHolder = "";
int a = 0;
while (a < sparseBooleanArray.size()) {
if (sparseBooleanArray.valueAt(a)) {
ValueHolder += ListViewItems[sparseBooleanArray.keyAt(a)] + ",";
}
a++;
}
ValueHolder = ValueHolder.replaceAll("(,)*$", "");
Toast.makeText(Trird.this, "ListView Selected Values = " + ValueHolder, Toast.LENGTH_LONG).show();
if(ValueHolder!=null){
taskList.remove(ValueHolder);
}
}
});
}
}
This is my adapter:
public class MyAdapter extends BaseAdapter {
private final Context context;//Context for view creation
private final String[] data;//raw data
public MyAdapter(String[] data, Context context){
this.data=data;
this.context=context;
}
//how many views to create
public int getCount() {
return data.length;
}
//item by index (position)
public Object getItem(int i) {
return data[i];
}
//ID => index of given item
public long getItemId(int i) {
return i;
}
//called .getCount() times - for each View of item in data
public View getView(int i, View recycleView, ViewGroup parent) {
if(recycleView==null)recycleView = LayoutInflater.from(context).inflate(R.layout.item,null);
((TextView)recycleView).setText(data[i]);//show relevant text in reused view
return recycleView;
}
}
And this is how looks like item in ListView.
<CheckedTextView
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="24sp"
android:drawableRight="?android:attr/listChoiceIndicatorMultiple"
/>
The problem is that you are trying removing item using the ListView remove methode.
Just remove the selected item from the list using the remove() method of your Adapter.
A possible way to do that would be:
CheckedTextView checkedText= baseAdapter.getItem([POSITION OF THE SELECTED ITEM]);
baseAdapter.remove(checkedText);
baseAdapter.notifyDataSetChanged();
put this code inside the wanted button click and there you go.
I am displaying a tab and which has two tags Category1 and Category2.
The list gets populated based on the data in the tables for category1 and category2. This works fine but to make the items in the listview respond in a certain way to clicks made on the listview is what I am stuck at. Like for example:
I am trying to make the checkbox become visible and checked when the user long clicks on the particular row in the listview. I am trying to code it but I have no idea where to begin. There aren't questions asked on this topic specifically. Below is the code that I have tried.
FragmentClass
public class CategoryFragment extends Fragment{
private ListView listView;
private String currentTabTag;
private CategoryAdapter1 categoryAdapter1;
private CategoryAdapter2 categoryAdapter2;
private MergeAdapter adapter = new MergeAdapter();
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
// inflate the layout for this fragment
View view = inflater.inflate(R.layout.activity_category_fragment_list, container, false);
// get references to widgets
listView = (ListView) view.findViewById (R.id.ListView);
// get the current tab
TabHost tabHost = (TabHost) container.getParent().getParent();
currentTabTag = tabHost.getCurrentTabTag();
// refresh the category list view
refreshCategoryList();
listView.setOnItemLongClickListener(this);
// return the view
return view;
}
public void refreshCategoryList() {
// get category list for current tab from database
Context context = getActivity().getApplicationContext();
CategoryDatabase categoryDatabase = new CategoryDatabase(context);
if(currentTabTag.equals("Category 1")) {
ArrayList<Category1> category1ArrayList = categoryDatabase.getAllCategory1(currentTabTag);
// create adapter and set it in the ListView widget
categoryAdapter1 = new Category1Adapter(context, category1ArrayList);
adapter.add(Category1Adapter);
}
else if(currentTabTag.equals("Category 2"))
{
ArrayList<Category2> category2ArrayList = categoryDatabase.getAllCategory2(currentTabTag);
// create adapter and set it in the ListView widget
categoryAdapter2 = new categoryAdapter2(context, category2ArrayList);
adapter.add(categoryAdapter2);
}
listView.setAdapter(adapter);
}
#Override
public void onResume() {
super.onResume();
refreshTaskList();
}
#Override
public boolean onItemLongClick(AdapterView<?> adapterView, View view, int i, long l) {
ItemsListLayout itemsListLayout = new ItemsListLayout(context);
switch (view.getId()) {
case R.id.listView: {
itemsListLayout.setMyListItems();
Toast.makeText(context,"Why cant I be called?",Toast.LENGTH_SHORT).show();
//registerForContextMenu(view);
}
/*default:
Intent intent = new Intent(context, MyCategoryAddEdit.class);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
//intent.putExtra("categoryId", category.getId());
intent.putExtra("editMode", true);
context.startActivity(intent);
break;*/
}
return false;
}
}
activity_category_fragment_list
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
xmlns:android="http://schemas.android.com/apk/res/android">
<ListView
android:id="#+id/prescriptorsListView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:longClickable="true"
android:descendantFocusability="blocksDescendants">
</ListView>
</LinearLayout>
ItemsListLayout
public class ItemsListLayout extends RelativeLayout implements View.OnCreateContextMenuListener {
private CheckBox completedCheckBox;
private TextView nameTextView;
private TextView notesTextView;
private ListView listView;
private CategoryDatabase categoryDatabase;
private Context context;
public ItemsListLayout(Context context) { // used by Android tools
super(context);
}
public ItemsListLayout(Context context, Prescription p) {
super(context);
// set context and get db object
this.context = context;
categoryDatabase = new CategoryDatabase(context);
// inflate the layout
LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
inflater.inflate(R.layout.activity_my_category_items_list_layout, this, true);
// get references to widgets
completedCheckBox = (CheckBox) findViewById(R.id.completedCheckBox);
completedCheckBox.setVisibility(GONE);
nameTextView = (TextView) findViewById(R.id.nameTextView);
notesTextView = (TextView) findViewById(R.id.notesTextView);
//listView = (ListView) findViewById(R.id.listView);
// set listeners
//listView.setOnItemLongClickListener(this);
// set task data on widgets
setCategory1(p);
}
public void setCategory1(setCategory1 p) {
setCategory1 = p;
nameTextView.setText(setCategory1.getCategory1Name());
// Remove the notes if empty
if (setCategory1.getMedicineName().equalsIgnoreCase("")) {
notesTextView.setVisibility(GONE);
}
else {
notesTextView.setText(setCategory1.getDescriptionName());
}
}
/*#Override
public boolean onItemLongClick(AdapterView<?> adapterView, View view, int i, long l) {
switch (view.getId()) {
case R.id.prescriptorsListView:
completedCheckBox.setVisibility(VISIBLE);
completedCheckBox.setChecked(true);
break;
default:
Intent intent = new Intent(context, MyCategoryAddEdit.class);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
//intent.putExtra("categoryId", category.getId());
intent.putExtra("editMode", true);
context.startActivity(intent);
break;
}
return false;
}
*/
public void setMyListItems()
{
completedCheckBox = (CheckBox) findViewById(R.id.completedCheckBox);
completedCheckBox.setVisibility(VISIBLE);
completedCheckBox.setChecked(true);
}
#Override
public void onCreateContextMenu(ContextMenu contextMenu, View view, ContextMenu.ContextMenuInfo contextMenuInfo)
{
super.onCreateContextMenu(contextMenu);
//MenuInflater inflater = this.context.
}
}
activity_my_category_items_list_layout
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/relativeLayoutTaskItem"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clickable="false"
android:descendantFocusability="blocksDescendants"
android:focusable="false"
android:focusableInTouchMode="false">
<CheckBox
android:id="#+id/completedCheckBox"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_alignParentTop="true"
android:layout_margin="5dp"
android:button="#drawable/btn_check"
android:textColor="#ffffff"
android:longClickable="true"
android:focusable="false"
android:clickable="false"
android:focusableInTouchMode="false"/>
<TextView
android:id="#+id/nameTextView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignBaseline="#+id/completedCheckBox"
android:layout_alignBottom="#+id/completedCheckBox"
android:layout_toRightOf="#+id/completedCheckBox"
android:text=""
android:textColor="#android:color/white"
android:textSize="20dp"
android:textStyle="bold"
android:longClickable="true"
android:focusable="false"
android:clickable="false"
android:textIsSelectable="false"
android:focusableInTouchMode="false"/>
<TextView
android:id="#+id/notesTextView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="#+id/nameTextView"
android:layout_marginTop="-5dp"
android:layout_toRightOf="#+id/completedCheckBox"
android:paddingBottom="7dp"
android:textColor="#android:color/white"
android:textSize="18dp"
android:longClickable="true"
android:focusable="false"
android:clickable="false"
android:textIsSelectable="false"
android:focusableInTouchMode="false"/>
</RelativeLayout>
Any kind of help is appreciated. Especially code snippets if possible :).
I suggest you to read this blog, it has step by step procedure of what exactly you want to achieve. You can even clone the project from github.
I'm wondering why having a ListView's layout_height="wrap_content" messes up Spinners at the end of the list. I ran through different ways of fixing it below. I'm hoping someone can explain the behaviour, or point out what android knowledge i'm lacking about drawing of views / ui events.
1) The problem visually can be seen here.
2) After changing the ListItem property
android:descendantFocusability="afterDescendants"
I get better behaviour but something is still going on. It simply seems like the items inside the list are not receiving the events, so that property change made sense to me.
Here is a video of how the spinners behave after updating that property.
All works fine except when I actually select an item.
3) After setting the ListView's layout_height="match_parent" the problem seems to go away after selecting an item. See here for that video.
The Activity:
public class SelectorActivity extends Activity {
public static final String TAG = SelectorActivity.class.getSimpleName();
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.v(TAG, "onCreate");
setContentView(R.layout.activity_selector);
LayoutInflater inflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
ListView contents = (ListView) findViewById(R.id.list_view);
contents.addHeaderView(new TestView(this));
contents.addFooterView(new View(this));
SimpleBaseAdapter listAdapter = new SimpleBaseAdapter(this);
// LOW RANGE
LinearLayout lowRange = (LinearLayout) inflater.inflate(R.layout.list_item_edit, null);
TextView lowRangeText = (TextView) lowRange.findViewById(R.id.text);
EditText lowRangeEditText = (EditText) lowRange.findViewById(android.R.id.edit);
// HIGH RANGE
LinearLayout highRange = (LinearLayout) inflater.inflate(R.layout.list_item_edit, null);
TextView highRangeText = (TextView) highRange.findViewById(R.id.text);
EditText highRangeEditText = (EditText) highRange.findViewById(android.R.id.edit);
// UNITS
LinearLayout units = (LinearLayout) inflater.inflate(R.layout.list_item_units, null);
TextView unitsText = (TextView) units.findViewById(android.R.id.text1);
// SPINNERS
LinearLayout spinners = (LinearLayout) inflater.inflate(R.layout.list_item_spinners, null);
Spinner spinner1 = (Spinner) spinners.findViewById(R.id.spinner1);
Spinner spinner2 = (Spinner) spinners.findViewById(R.id.spinner2);
Spinner spinner3 = (Spinner) spinners.findViewById(R.id.spinner3);
DebugAdapterViewListeners.set(spinner1, "spinner1");
// VIEW SETUP
lowRangeText.setText("text1");
highRangeText.setText("text2");
unitsText.setText("text3");
// SPINNER SETUP
String[] massUnits1 = new String[]{"one","two"};
String[] massUnits2 = new String[]{"three","four"};
String[] timeUnits = new String[]{"five","six"};
ArrayAdapter<String> adapt1 = new ArrayAdapter<String>(this, R.layout.spinner_list_item_centered);
ArrayAdapter<String> adapt2 = new ArrayAdapter<String>(this, R.layout.spinner_list_item_centered);
ArrayAdapter<String> adapt3 = new ArrayAdapter<String>(this, R.layout.spinner_list_item_centered);
adapt1.addAll(massUnits1);
adapt2.addAll(massUnits2);
adapt3.addAll(timeUnits);
spinner1.setAdapter(adapt1);
spinner2.setAdapter(adapt2);
spinner3.setAdapter(adapt3);
listAdapter.addView(lowRange);
listAdapter.addView(highRange);
listAdapter.addView(units);
listAdapter.addView(spinners);
contents.setAdapter(listAdapter);
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.selector, menu);
return false;
}
}
Here is the SimpleBaseAdapter class:
public class SimpleBaseAdapter extends BaseAdapter {
private ArrayList<View> views;
private Context context;
public SimpleBaseAdapter(Context context) {
this.context = context;
this.views = new ArrayList<View>();
}
public void addView(View view) {
this.views.add(view);
}
#Override
public int getCount() {
return views.size();
}
#Override
public Object getItem(int position) {
View view = views.get(position);
if (view instanceof AbsListView) {
return ((AbsListView)view).getItemAtPosition(position);
} else if (view instanceof AbsSpinner) {
return ((AbsSpinner)view).getItemAtPosition(position);
} else {
return null;
}
}
#Override
public long getItemId(int position) {
View view = views.get(position);
if (view instanceof AbsListView) {
return ((AbsListView)view).getItemIdAtPosition(position);
} else if (view instanceof AbsSpinner) {
return ((AbsSpinner)view).getItemIdAtPosition(position);
} else {
return 0;
}
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
return views.get(position);
}
}
Activity layout:
<LinearLayout 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:background="#color/green_1"
android:orientation="vertical"
>
<ListView
android:id="#+id/list_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:headerDividersEnabled="true"
android:footerDividersEnabled="true"
android:dividerHeight="0.5sp"
android:divider="#color/black"
android:clipToPadding="false"
android:layout_marginTop="18sp"
android:layout_marginBottom="18sp"
/>
</LinearLayout>
Edit list item layout:
<?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"
android:padding="#dimen/row_padding"
android:background="#android:color/white"
>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="#+id/text"
android:layout_weight="50"
android:gravity="top"
android:textSize="#dimen/font_size_standard"
android:textColor="#drawable/selector_row_item_detail_text"
/>
<EditText
android:layout_width="0dip"
android:layout_height="wrap_content"
android:id="#android:id/edit"
android:layout_weight="50"
android:inputType="number"
android:gravity="right"
/>
</LinearLayout>
The spinner row item layout:
<?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:id="#+id/spinner_container"
android:orientation="horizontal"
android:background="#android:color/white"
android:paddingTop="#dimen/header_row_padding_vertical"
>
<Spinner
android:layout_width="0dip"
android:layout_height="wrap_content"
android:layout_weight="33"
android:id="#+id/spinner1"
android:gravity="center"
android:spinnerMode="dropdown"
/>
<Spinner
android:layout_width="0dip"
android:layout_height="wrap_content"
android:layout_weight="33"
android:id="#+id/spinner2"
android:gravity="center"
android:spinnerMode="dialog"
/>
<Spinner
android:layout_width="0dip"
android:layout_height="wrap_content"
android:layout_weight="33"
android:id="#+id/spinner3"
android:gravity="center"
android:spinnerMode="dialog"
/>
</LinearLayout>
The problem that you were facing was the basic behaviour of Spinner, so a modified Spinner is needed. This is code for the spinner whose initial ie. by default the visualisation is "Select Item" (if the prompt declared in the .xml like android:prompt="#string/Select Item") & the dropdown views are the same size of the original spinner. The limitation of this modified spinner is that it does not display the prompt if the items are empty.
Make a new class named NoDefaultSpinner.java & in that copy paste this code
public class NoDefaultSpinner extends Spinner {
public NoDefaultSpinner(Context context) {
super(context);
}
public NoDefaultSpinner(Context context, AttributeSet attrs) {
super(context, attrs);
}
public NoDefaultSpinner(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
#Override
public void setAdapter(SpinnerAdapter orig ) {
final SpinnerAdapter adapter = newProxy(orig);
super.setAdapter(adapter);
try {
final Method m = AdapterView.class.getDeclaredMethod(
"setNextSelectedPositionInt",int.class);
m.setAccessible(true);
m.invoke(this,-1);
final Method n = AdapterView.class.getDeclaredMethod(
"setSelectedPositionInt",int.class);
n.setAccessible(true);
n.invoke(this,-1);
}
catch( Exception e ) {
throw new RuntimeException(e);
}
}
protected SpinnerAdapter newProxy(SpinnerAdapter obj) {
return (SpinnerAdapter) java.lang.reflect.Proxy.newProxyInstance(
obj.getClass().getClassLoader(),
new Class[]{SpinnerAdapter.class},
new SpinnerAdapterProxy(obj));
}
/**
* Intercepts getView() to display the prompt if position < 0
*/
protected class SpinnerAdapterProxy implements InvocationHandler {
protected SpinnerAdapter obj;
protected Method getView;
protected SpinnerAdapterProxy(SpinnerAdapter obj) {
this.obj = obj;
try {
this.getView = SpinnerAdapter.class.getMethod(
"getView",int.class,View.class,ViewGroup.class);
}
catch( Exception e ) {
throw new RuntimeException(e);
}
}
public Object invoke(Object proxy, Method m, Object[] args) throws Throwable {
try {
return m.equals(getView) &&
(Integer)(args[0])<0 ?
getView((Integer)args[0],(View)args[1],(ViewGroup)args[2]) :
m.invoke(obj, args);
}
catch (InvocationTargetException e) {
throw e.getTargetException();
}
catch (Exception e) {
throw new RuntimeException(e);
}
}
protected View getView(int position, View convertView, ViewGroup parent)
throws IllegalAccessException {
if( position<0 ) {
final TextView v =
(TextView) ((LayoutInflater)getContext().getSystemService(
Context.LAYOUT_INFLATER_SERVICE)).inflate(
android.R.layout.simple_spinner_item,parent,false);
v.setText(getPrompt());
return v;
}
return obj.getView(position,convertView,parent);
}
}
}
In spinner row item layout change the type of spinner to <com.example.appname.NoDefaultSpinner like this
<com.example.appname.NoDefaultSpinner
android:layout_width="0dip"
android:layout_height="wrap_content"
android:layout_weight="33"
android:id="#+id/spinner1"
android:gravity="center"
android:spinnerMode="dropdown"
/>
<com.example.appname.NoDefaultSpinner
android:layout_width="0dip"
android:layout_height="wrap_content"
android:layout_weight="33"
android:id="#+id/spinner2"
android:gravity="center"
android:spinnerMode="dialog"
/>
<com.example.appname.NoDefaultSpinner
android:layout_width="0dip"
android:layout_height="wrap_content"
android:layout_weight="33"
android:id="#+id/spinner3"
android:gravity="center"
android:spinnerMode="dialog"
/>
The Activity : Change the type of Spinner to NoDefaultSpinner like this
public class SelectorActivity extends Activity {
public static final String TAG = SelectorActivity.class.getSimpleName();
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.v(TAG, "onCreate");
setContentView(R.layout.activity_selector);
LayoutInflater inflater = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE);
ListView contents = (ListView) findViewById(R.id.list_view);
contents.addHeaderView(new TestView(this));
contents.addFooterView(new View(this));
SimpleBaseAdapter listAdapter = new SimpleBaseAdapter(this);
// LOW RANGE
LinearLayout lowRange = (LinearLayout) inflater.inflate(R.layout.list_item_edit, null);
TextView lowRangeText = (TextView) lowRange.findViewById(R.id.text);
EditText lowRangeEditText = (EditText) lowRange.findViewById(android.R.id.edit);
// HIGH RANGE
LinearLayout highRange = (LinearLayout) inflater.inflate(R.layout.list_item_edit, null);
TextView highRangeText = (TextView) highRange.findViewById(R.id.text);
EditText highRangeEditText = (EditText) highRange.findViewById(android.R.id.edit);
// UNITS
LinearLayout units = (LinearLayout) inflater.inflate(R.layout.list_item_units, null);
TextView unitsText = (TextView) units.findViewById(android.R.id.text1);
// SPINNERS
LinearLayout spinners = (LinearLayout) inflater.inflate(R.layout.list_item_spinners, null);
NoDefaultSpinner spinner1 = (NoDefaultSpinner) spinners.findViewById(R.id.spinner1);
NoDefaultSpinner spinner2 = (NoDefaultSpinner) spinners.findViewById(R.id.spinner2);
NoDefaultSpinner spinner3 = (NoDefaultSpinner) spinners.findViewById(R.id.spinner3);
DebugAdapterViewListeners.set(spinner1, "spinner1");
// VIEW SETUP
lowRangeText.setText("text1");
highRangeText.setText("text2");
unitsText.setText("text3");
// SPINNER SETUP
String[] massUnits1 = new String[]{"one","two"};
String[] massUnits2 = new String[]{"three","four"};
String[] timeUnits = new String[]{"five","six"};
ArrayAdapter<String> adapt1 = new ArrayAdapter<String>(this, R.layout.spinner_list_item_centered);
ArrayAdapter<String> adapt2 = new ArrayAdapter<String>(this, R.layout.spinner_list_item_centered);
ArrayAdapter<String> adapt3 = new ArrayAdapter<String>(this, R.layout.spinner_list_item_centered);
adapt1.addAll(massUnits1);
adapt2.addAll(massUnits2);
adapt3.addAll(timeUnits);
spinner1.setAdapter(adapt1);
spinner2.setAdapter(adapt2);
spinner3.setAdapter(adapt3);
listAdapter.addView(lowRange);
listAdapter.addView(highRange);
listAdapter.addView(units);
listAdapter.addView(spinners);
contents.setAdapter(listAdapter);
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.selector, menu);
return false;
}
This solution relies on reflection to call the AdapterView.setNextSelectedPositionInt() and AdapterView.setSelectedPositionInt(), & runs successfully on API 4 to API 19.
You should extend SpinnerAdapter rather than BaseAdapter. It has getDropdownView() as well as getView() and i believe it handles some special cases internally itself. I am extending this adapter in a similar layout on android 4.2 and I don't see the issues you have.
I would hazzard a guess that a difference in how getDropdownView() handles attaching the view to the root would account for this difference but I haven't looked into the code to check this
I would like to get a click anywhere on the row of my ListActivity to proceed to another Activity. Currently, i am able to see the row position in a log when the ImageView is clicked, but the click is not registered anywhere else in the row. Furthermore, the Toast command crashes the program. Any assistance toward my stated goal would be much appreciated. Thanks!
event_row.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="?android:attr/listPreferredItemHeight"
android:padding="6dip">
<ImageView
android:id="#+id/icon"
android:layout_width="wrap_content"
android:layout_height="fill_parent"
android:layout_marginRight="6dip"
android:contentDescription="#string/event_icon_desc"
android:src="#drawable/event_icon" />
<LinearLayout
android:orientation="vertical"
android:layout_width="0dip"
android:layout_weight="1"
android:layout_height="fill_parent">
<TextView
android:id="#+id/event_tv"
android:layout_width="fill_parent"
android:layout_height="0dip"
android:layout_weight="1"
android:textIsSelectable="true" />
<TextView
android:id="#+id/location_tv"
android:layout_width="fill_parent"
android:layout_height="0dip"
android:layout_weight="1"
android:singleLine="true"
android:textIsSelectable="true" />
<TextView
android:id="#+id/date_tv"
android:layout_width="fill_parent"
android:layout_height="0dip"
android:layout_weight="1"
android:singleLine="true"
android:ellipsize="marquee"
android:textIsSelectable="true" />
</LinearLayout>
</LinearLayout>
display_events_activity.xml
<?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:id="#+id/event_ll" >
<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="fill_parent"
android:layout_height="fill_parent"
android:text="#string/tv_empty" />
</LinearLayout>
DisplayEvents.java:
public class DisplayEvents extends ListActivity {
private ProgressDialog progressDialog = null;
private ArrayList<Event> events = new ArrayList<Event>();
private EventAdapter eventAdapter;
private Runnable viewEvents;
//private final int ITEMS_PER_VIEW = 10;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.disp_events_activity);
new EventTask().execute();
this.eventAdapter = new EventAdapter(this, R.layout.event_row, this.events);
setListAdapter(this.eventAdapter);
viewEvents = new Runnable() {
#Override
public void run() {
getEvents();
}
};
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.activity_main, menu);
return true;
}
private void getEvents() {
runOnUiThread(adaptEvents);
}
private Runnable adaptEvents = new Runnable() {
#Override
public void run() {
if (events != null && events.size() > 0) {
eventAdapter.notifyDataSetChanged();
for (int i=0; i<events.size(); i++)
eventAdapter.add(events.get(i));
}
progressDialog.dismiss();
eventAdapter.notifyDataSetChanged();
}
};
private class EventAdapter extends ArrayAdapter<Event> {
private ArrayList<Event> events;
public EventAdapter(Context context, int textViewResourceId, ArrayList<Event> events) {
super(context, textViewResourceId, events);
this.events = events;
}
#Override
public View getView(final int position, View convertView, ViewGroup parent) {
View v = convertView;
if (v == null) {
LayoutInflater li = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE);
v = li.inflate(R.layout.event_row, null);
}
Event event = events.get(position);
if (event != null) {
TextView event_tv = (TextView) v.findViewById(R.id.event_tv);
TextView loc_tv = (TextView) v.findViewById(R.id.location_tv);
TextView date_tv = (TextView) v.findViewById(R.id.date_tv);
if (event_tv != null)
event_tv.setText("Event: " + event.getEventName());
if (loc_tv != null)
loc_tv.setText("Location: " + event.getPlace() + " - " + event.getCity());
if (date_tv != null)
date_tv.setText("Date: " + event.getDate());
}
v.setOnClickListener( new OnClickListener() {
public void onClick(View v) {
Log.v("text", "Image clicked, row %d"+position);
Toast.makeText(DisplayEvents.this, position, Toast.LENGTH_LONG).show();
}
});
return v;
}
}
public class EventTask extends AsyncTask<Void, Void, ArrayList<Event>> {
#Override
protected ArrayList<Event> doInBackground(Void... Void) {
ArrayList<Event> dbEvents = new ArrayList<Event>();
BuyTicketsConnection buyConnection = new BuyTicketsConnection();
dbEvents = buyConnection.getEventsList();
return dbEvents;
}
protected void onPostExecute(ArrayList<Event> dbEvents) {
events = dbEvents;
Thread thread = new Thread(null, viewEvents, "GetEventsBackground");
thread.start();
progressDialog = ProgressDialog.show(DisplayEvents.this, "Please wait...", "Retrieving events...", true);
}
}
}
EDIT:
I've now got this method:
#Override
protected void onListItemClick (ListView l, View v, int position, long id){
Log.v("text", "Image clicked, row %d"+position);
Toast.makeText(DisplayEvents.this, position, Toast.LENGTH_LONG).show();
}
in my DisplayEvents (ListActivity) class, but still no dice. Also, i'd like the click to respond to anything in that list item (note there are many views in that row layout which should inherit the click event handler).
it seems that the issue is how i should get all of the views (ImageView and 3 TextViews) in event_row.xml to get associated with this OnListItemClick handler. how is this entire row (comprised of 4 views in 2 LinearLayouts) recognized in the context of that OnListItemClick handler?
Use setOnItemClickListener on your DisplayEvents Activity and remove setOnClickListener from your EventAdapter.
The problem is that some of your item views are focusable and/or clickable (thus they eat the click events and that causes the ListView to not be able to receive the event in order to know that you clicked on a list item). For a list to trigger item-click events, it must be able to receive the touch events without them being eaten by the children.
In your item layout, you have 3 TextViews that all have textIsSelectable=true, and that means the TextView itself will eat the touch events. You need to disable that in order to trigger the onListItemClick() events.
I encountered this problem and removing
android:textIsSelectable="true"
will solve it.
I am using a ListView with a custom adapter for displaying items as pairs of title/subtitle TextViews.
Unfortunately, I can select an item just by clicking on its upper half, the one occupied by the title
Here is the code I am using:
journal_list.xml
<?xml version="1.0" encoding="utf-8"?>
<ListView
android:id="#android:id/list"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:drawSelectorOnTop="false"
android:background="#fff"
android:cacheColorHint="#fff"
android:paddingBottom="6dp"
/>
journal_list_item.xml for the list item's layout
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:descendantFocusability="blocksDescendants"
>
<TextView
android:id="#+id/journal_entry_date"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:textStyle="bold"
android:textColor="#000"
android:textSize="18sp"
android:layout_alignParentTop="true"
android:layout_alignParentLeft="true"
/>
<TextView
android:id="#+id/journal_content"
android:paddingLeft="28dp"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:textSize="16sp"
android:textColor="#555"
android:inputType="textMultiLine"
android:maxLines="2"
android:minLines="1"
android:layout_below="#id/journal_entry_date"
android:layout_alignParentLeft="true"
/>
Also the code for the adapter:
private class JournalAdapter extends ArrayAdapter<JournalEntry> {
Context ctxt;
public JournalAdapter(Context ctxt) {
super(ctxt, R.layout.journal_list_item);
this.ctxt = ctxt;
}
public View getView(int position, View convertView, ViewGroup parent) {
View row = convertView;
JournalHolder holder;
if (row == null) {
row = ((LayoutInflater)ctxt.getSystemService(Context.LAYOUT_INFLATER_SERVICE)).
inflate(R.layout.journal_list_item, parent, false);
holder = new JournalHolder(row);
row.setTag(holder);
} else {
holder = (JournalHolder) row.getTag();
}
JournalEntry crt = getItem(position);
holder.getDate().setText(crt.dateWritten);
holder.getContent().setText(crt.content);
return row;
}
}
private static class JournalHolder {
private TextView date;
private TextView content;
private View base;
public JournalHolder(View base) {
this.base = base;
}
public TextView getDate() {
if (date == null)
date = (TextView) base.findViewById(R.id.journal_entry_date);
return date;
}
public TextView getContent() {
if (content == null)
content = (TextView) base.findViewById(R.id.journal_content);
return content;
}
}
Code from the ListActivity's onCreate() method:
private JournalAdapter adapter;
#Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
setContentView(R.layout.journal_list);
adapter = new JournalAdapter(this);
setListAdapter(adapter);
registerForContextMenu(getListView());
}
Also, I am calling a method called updateList() in onResume()
private void updateList() {
adapter.clear();
Cursor cursor = helper.listJournal(start, end);
cursor.moveToFirst();
JournalEntry crt;
while (!cursor.isAfterLast()) {
crt = new JournalEntry();
crt.dateWritten = cursor.getString(cursor.getColumnIndex("date_written"));
crt.content = cursor.getString(cursor.getColumnIndex("content"));
crt.id = cursor.getInt(cursor.getColumnIndex("entry_id"));
adapter.add(crt);
cursor.moveToNext();
}
cursor.close();
adapter.notifyDataSetChanged();
}
List item clicking is handled in the onListItemClick of my ListActivity, as follows:
#Override
public void onListItemClick(ListView l, View v, int position, long id) {
Intent intent = new Intent(this, JournalEditor.class);
intent.setAction(Intent.ACTION_EDIT);
intent.putExtra("entry_id", adapter.getItem(position).id);
startActivity(intent);
}
Apparently, I am forgetting something important, and that's why this whole weird thing happens.
I have found a similar discussion here: http://groups.google.com/group/android-developers/browse_thread/thread/5636c8ea74033657 but it wasn't very helpful.
The problem may be from the clicking of the textviews. When you set the views of the listview items as clickable, they override the click of the listview item and the click will only work when the specific view of the item is clicked.
If this is the case, do the following for your two textviews in the getView() function:
TextView tv;
tv.setFocusable(false);
tv.setClickable(false);
Then set the listview to clickable: listView1.setClickable(true);