TextView breaks my word by letters - java

My requirements: create "incoming bubble" with width by content and max width 90%.
I have this markup:
<?xml version="1.0" encoding="utf-8"?>
<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="wrap_content"
android:orientation="horizontal"
android:weightSum="1.0"
tools:background="#color/white_smoke">
<LinearLayout
android:id="#+id/flBubble"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_gravity="start"
android:background="#drawable/bubble_in"
android:layout_weight="0.9">
<ImageView
android:id="#+id/ivSay"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="?android:attr/selectableItemBackground"
android:contentDescription="#string/default_content_description"
android:padding="8dp"
android:src="#drawable/ic_play_circle_outline_black_24dp"
android:tint="#color/primary"/>
<TextView
android:id="#+id/tvValue"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:padding="8dp"
android:textColor="#color/black"
android:textSize="16sp"
tools:text="I would like to go to an Italian restaurant"/>
</LinearLayout>
<View
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_weight="0.1"/>
</LinearLayout>
Sometimes I get the following result:
But I expect the following result (it's falsely encouraging screenshot from Android Studio preview):
How can I prevent breaking word restaraunt by letters?
UPDATE
Although I use minSdk=15 I tried to use breakStrategy and I haven't get expected result.
android:breakStrategy="simple":
android:breakStrategy="balanced":
I found a related question: Force next word to a new line if the word is too long for the textview, but I didn't undestand how can I get maximum available width for TextView with layout_width="wrap_content?
It would be great if I could override the TextView.setText and place line breaks there if needed.

OMG, there were in my string!
value.replaceAll("\\s", " ");
Thank you all!

Use MaxWidth property for textview or else you should provide width for textview
<com.custom.views.CustomTextView
android:id="#+id/txt_send_chat"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:gravity="center_vertical"
android:maxWidth="250dp"
android:textColor="#color/color_chat_sender"
android:textSize="16sp"
app:font_name="#string/font_roboto_regular" />

You can use webview to achieve this behavior.
In webview you can use css to adjust text.
Take a look at this answer
Update
You can calculate width of string and add \n to string where is string needs to split
Rect bounds = new Rect();
Paint textPaint = textView.getPaint();
textPaint.getTextBounds(text, 0, text.length(), bounds);
int height = bounds.height();
int width = bounds.width();
Results is in pixels, so just check width of your view or screen and split the string.
UPDAE2: Example Code
I just wrote an example with simple layout in activity onCreate you can implement it in adapter or whatever works for you.
TextView textView = (TextView) findViewById(R.id.txt); //textview with empty text
Rect bounds = new Rect();
Paint textPaint = textView.getPaint();
String text = "some long text here.....";// text data to work on
textPaint.getTextBounds(text, 0, text.length(), bounds);
int textWidth = bounds.width();// get text width in pixel
int marginPadding = 100;// we have some padding and margin from xml layouts
DisplayMetrics displayMetrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);
int rootWidth = displayMetrics.widthPixels-marginPadding;// maximum width on screan
if (textWidth > rootWidth) { // check if need to split the string.
int lineMax = (text.length() * rootWidth) / textWidth; // maximum Characters for each line
String result = text.replaceAll("(.{" + String.valueOf(lineMax) + "})", "$1\n"); // regex to replace each group(lineMax) of Chars with group of char + new line
textView.setText(result);
} else
textView.setText(text);
UPDATE#3: Fixed code for Listview
onCreate
ArrayList<String> data = new ArrayList<>();
data.add("000");
data.add("aaaaaaaaaaa");
data.add("aaaaaaaaaaa bbbbbbbbbbbb");
data.add("aaaaaaaaaaa bbbbbbbbbbbb cccccccccccccccc");
data.add("aaaaaaaaaaa bbbbbbbbbbbb cccccccccccccccc ddddddddddddd");
data.add("aaaaaaaaaaa bbbbbbbbbbbb cccccccccccccccc ddddddddddddd eeeeeeeeeeeee");
data.add("aaaaaaaaaaa bbbbbbbbbbbb cccccccccccccccc ddddddddddddd eeeeeeeeeeeee ffffffffffffffffff");
data.add("aaaaaaaaaaa bbbbbbbbbbbb cccccccccccccccc ddddddddddddd eeeeeeeeeeeee ffffffffffffffffff gggggggggggggggg");
data.add("aaaaaaaaaaa bbbbbbbbbbbb cccccccccccccccc ddddddddddddd eeeeeeeeeeeee ffffffffffffffffff gggggggggggggggg hhhhhhhhhhhhhhhh");
ListView listView = (ListView) findViewById(R.id.listview);
MyAdapter adapter= new MyAdapter(data,this);
listView.setAdapter(adapter);
adapter.notifyDataSetChanged();
MyAdapter.java
public class MyAdapter extends BaseAdapter {
private LayoutInflater inflater = null;
Context context;
ArrayList<String> data;
public MyAdapter(ArrayList<String> data, Context context) {
this.context = context;
this.data = data;
inflater = (LayoutInflater) context
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
}
#Override
public int getCount() {
return data.size();
}
#Override
public Object getItem(int i) {
return data.get(i);
}
#Override
public long getItemId(int i) {
return i;
}
#Override
public View getView(final int i, View convertView, ViewGroup viewGroup) {
final View view = inflater.inflate(R.layout.item, null);
final TextView tv_text = (TextView) view.findViewById(R.id.tvValue);
if (data.get(i) != null) {
tv_text.post(new Runnable() {
#Override
public void run() {
//TextView is Ready to be used.
fixText(data.get(i),tv_text);
}
});
}
return view;
}
private void fixText(String text, TextView textView) {
Rect bounds = new Rect();
Paint textPaint = textView.getPaint();
textPaint.getTextBounds(text, 0, text.length(), bounds);
int textWidth = bounds.width();// get text width in pixel
int marginPadding = 100;// we have some padding and margin from xml layouts
DisplayMetrics displayMetrics = new DisplayMetrics();
((MainActivity) context).getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);
int rootWidth = textView.getWidth();//displayMetrics.widthPixels - marginPadding;// maximum width on screan
if (textWidth > rootWidth) { // check if need to split the string.
//int lineMax = (text.length() * rootWidth) / textWidth; // maximum Characters for each line
//String result = text.replaceAll("(.{" + String.valueOf(lineMax-5) + "})", "$1\n"); // regex to replace each group(lineMax) of Chars with group of char + new line
String result = wrapText(rootWidth,text);
textView.setText(result);
} else
textView.setText(text);
}
private String wrapText(int textviewWidth,String mQuestion) {
String temp = "";
String sentence = "";
String[] array = mQuestion.split(" "); // split by space
for (String word : array) {
if ((temp.length() + word.length()) < textviewWidth) { // create a temp variable and check if length with new word exceeds textview width.
temp += " "+word;
} else {
sentence += temp+"\n"; // add new line character
temp = word;
}
}
return (sentence.replaceFirst(" ", "")+temp);
}
item.xml
<?xml version="1.0" encoding="utf-8"?>
<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="wrap_content"
android:orientation="horizontal"
android:weightSum="1.0"
tools:background="#color/colorAccent">
<LinearLayout
android:id="#+id/flBubble"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_gravity="start"
android:background="#color/colorPrimary"
android:layout_weight="0.9">
<ImageView
android:id="#+id/ivSay"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="?android:attr/selectableItemBackground"
android:contentDescription="default_content_description"
android:padding="8dp"
android:src="#android:drawable/ic_media_play"
android:tint="#color/colorPrimaryDark" />
<TextView
android:id="#+id/tvValue"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:padding="8dp"
android:textColor="#000000"
android:textSize="16sp"
tools:text="I would like to go to an Italian restaurant jkjk l;'"/>
</LinearLayout>
<View
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_weight="0.1"/>
</LinearLayout>

Try this
<TextView
android:id="#+id/tvValue"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:padding="8dp"
android:textColor="#color/black"
android:textSize="16sp"
tools:text="I would like to go to an Italian restaurant"/>
</LinearLayout>

You can try with
Autosizing TextViews
The Support Library 26.0 provides full support to the autosizing TextView feature on devices running Android versions prior to Android 8.0 (API level 26). The library provides support to Android 4.0 (API level 14) and higher. The android.support.v4.widget package contains the TextViewCompat class to access features in a backward-compatible fashion
For Example:
<TextView
android:layout_width="match_parent"
android:layout_height="200dp"
android:autoSizeTextType="uniform" />
For more details Guidelines go HERE
Their is Library too HERE

Try this
private String getWidthFitString(String input) {
Paint paint = text.getPaint();
// you can define max width by your self
int maxWidth = getContentMaxWidth();
float width = paint.measureText(input);
if (width > maxWidth) {
List<String> words = Arrays.asList(input.split("\\s"));
int breakLinePosition = 0;
String toBreakLineText;
List<String> toBreakLineWords = new ArrayList<>();
while (breakLinePosition < words.size()) {
toBreakLineWords.add(words.get(breakLinePosition));
toBreakLineText = TextUtils.join(" ", toBreakLineWords);
float currentWidth = paint.measureText(toBreakLineText);
if (currentWidth > maxWidth) {
break;
}
breakLinePosition ++;
}
if (breakLinePosition > 1) {
toBreakLineWords.remove(toBreakLineWords.size() - 1);
toBreakLineText = TextUtils.join(" ", toBreakLineWords);
List<String> fromBreakLineWords = new ArrayList<>();
for (int i = breakLinePosition; i < words.size(); i++) {
fromBreakLineWords.add(words.get(i));
}
return toBreakLineText + "\n" + getWidthFitString(TextUtils.join(" ", fromBreakLineWords));
} else {
return input;
}
}
return input;
}

For those whose string has non-breaking space character may try the following:
value.replace("\u00A0", " ")
Hope this might help

Change your TextView to EditText and put this 2 lines. it should help you
android:inputType="textMultiLine"
android:enabled="false"
This will place you text properly and later on you can give a edit feature in your application if you need.

Related

Text wrap around image in fragments

Im trying to set wrap text around image in one of my Fragments but it isn't working. Can somebody help me to solve this issue?
code in xml Fragment4
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="make.appaplication.Fragment4">
<!-- TODO: Update blank fragment layout -->
<ImageView
android:layout_width="150dp"
android:layout_height="150dp"
android:src="#drawable/nature"
android:id="#+id/imageView"
android:layout_weight="1"
android:layout_marginRight="17dp"
android:layout_marginEnd="17dp"
android:layout_alignParentTop="true"
android:layout_alignParentRight="true"
android:layout_alignParentEnd="true" />
<TextView
android:id="#+id/textView3"
android:textSize="17dp"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="#string/text_page_1"
android:layout_below="#+id/textView5"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true" />
code in class MyLeadingMarginSpan2
public class MyLeadingMarginSpan2 implements LeadingMarginSpan.LeadingMarginSpan2 {
private int margin;
private int lines;
private boolean wasDrawCalled = false;
private int drawLineCount = 0;
public MyLeadingMarginSpan2(int lines, int margin) {
this.margin = margin;
this.lines = lines;
}
#Override
public int getLeadingMargin(boolean first) {
boolean isFirstMargin = first;
// a different algorithm for api 21+
if (Build.VERSION.SDK_INT >= 21) {
this.drawLineCount = this.wasDrawCalled ? this.drawLineCount + 1 : 0;
this.wasDrawCalled = false;
isFirstMargin = this.drawLineCount <= this.lines;
}
return isFirstMargin ? this.margin : 0;
}
#Override
public void drawLeadingMargin(Canvas c, Paint p, int x, int dir, int top, int baseline, int bottom, CharSequence text, int start, int end, boolean first, Layout layout) {
this.wasDrawCalled = true;
}
#Override
public int getLeadingMarginLineCount()
{
return this.lines;
}
}
and code in class Fragment4
String text = getString(R.string.text_page_1);
SpannableString ss = new SpannableString(text);
Drawable dIcon = ContextCompat.getDrawable(getActivity(),R.drawable.nature);
int rightMargin = dIcon.getIntrinsicWidth() + 10;
ss.setSpan(new MyLeadingMarginSpan2(3, rightMargin), 0, ss.length(), 0);
final TextView messageView = (TextView) view.findViewById(R.id.textView3);
messageView.setText(ss);
Here is what I have now

How to get text from TextView inside a CardView?

Inside a CardView I have a LinearLayout with three TextViews in it and I want to get the text from these TextViews. My XML-file looks like this:
<LinearLayout
android:id="#+id/defense"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:orientation="horizontal"
>
<android.support.v7.widget.CardView
android:id="#+id/cardCB"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_gravity="bottom"
android:layout_weight="1"
app:cardBackgroundColor="#android:color/transparent"
app:cardElevation="0dp">
<LinearLayout
android:id="#+id/innerLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:text="Rannochia"
android:textAlignment="center"
android:textStyle="bold"/>
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:text="78"
android:textAlignment="center"
android:textStyle="bold"/>
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:text="CB"
android:textAlignment="center"
android:textStyle="bold"/>
</LinearLayout>
</android.support.v7.widget.CardView>
</LinearLayout>
Then I want to iterate through all CardViews in the parent LinearLayout and get the TextViews. Something like this:
for (int i = 0; i < defense.getChildCount(); i++){
String getName = ((CardView)defense.getChildAt(i)).getText().toString();
id[i] = getName;
}
But I'm not able to call the methods getText().toString(); like this. How can I get the text from these TextViews inside the CardView?
So many options in here.
1. Give id for your TextViews
2. Use for in LinearLayout with id innerLayout
3. When your UI is dynamic and you need to get TextViews from defense
for (int i = 0; i < defense.getChildCount(); i++){
CardView card = defense.getChildAt(i);
ViewGroup viewGroup = ((ViewGroup)card.getChildAt(0));
for(int j=0;j<viewGroup.getChildCount();j++){
String getName = ((TextView)viewGroup.getChildAt(j)).getText().toString();
id[i] = getName;
}
}
Just give the reference to TextView
for (int i = 0; i < defense.getChildCount(); i++){
String getName = ((TextView)defense.getChildAt(i)).getText().toString();
id[i] = getName;
}
It not works because the textview is inside another ViewGroup which is #+id/innerLayout..
i make a simple function for you..
private void loopViews(ViewGroup view) {
for (int i = 0; i < view.getChildCount(); i++) {
View v = view.getChildAt(i);
if (v instanceof EditText) {
// will be executed when its edittext
Log.d("Check", "This is EditText");
} else if (v instanceof TextView) {
// will be executed when its textview,, and get the text..
TextView x = (TextView) v;
String aa = x.getText().toString();
Log.d("Check", "This is TextView with text : " +aa);
} else if (v instanceof ViewGroup) {
// will be executed when its viewgroup,, and loop it for get the child view..
Log.d("Check", "This is ViewGroup");
this.loopViews((ViewGroup) v);
}
}
and you can use it like this..
LinearLayout def = (LinearLayout) findViewById(R.id.defense);
loopViews(def);
hope it can help you.. :)

How to Change Text Color, Size and Font in a Dynamically Created ListView

I am trying to make a to-do list using an EditText and a ListView. How can I change the text font, color and size? I have seen a couple answers using array adapters, but don't know how to apply them to dynamically created ListView items.
Here is what I have so far:
ActivityMain.xml
<RelativeLayout
android:id="#+id/AgendaRL"
android:orientation="vertical"
android:background="#3E2723"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="#+id/agenda"
android:layout_width="370sp"
android:layout_height="wrap_content"
android:text="#string/agenda"
android:textSize="40sp"
android:textColor="#b7950b"
android:layout_marginTop="12sp"
android:layout_marginLeft="12sp"
android:layout_marginStart="12sp"
android:layout_marginBottom="0sp" />
<View
android:background="#b7950b"
android:layout_below="#+id/agenda"
android:layout_width="28sp"
android:layout_height="36sp"/>
<EditText
android:id="#+id/aTask"
android:layout_below="#+id/agenda"
android:background="#drawable/ribbon"
android:inputType="text"
android:text="#string/Add_Task"
android:textColor="#3E2723"
android:maxLength="22"
android:maxLines="1"
android:layout_width="330sp"
android:layout_height="36sp"
android:textSize="28sp"
android:layout_marginLeft="28sp"
android:layout_marginStart="28sp"/>
<Button
android:id="#+id/Done"
style="?android:attr/borderlessButtonStyle"
android:layout_marginLeft="250sp"
android:layout_marginStart="250sp"
android:background="#b7950b"
android:text="#string/Done"
android:textColor="#3E2723"
android:textSize="18sp"
android:layout_below="#+id/agenda"
android:layout_width="48sp"
android:layout_height="36sp"
android:onClick="DoneClick"/>
<ListView
android:id="#+id/LVAgenda"
android:layout_below="#+id/aTask"
android:divider="#android:color/transparent"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
</RelativeLayout>
MainActivity.Java
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ListView LVAgenda = (ListView) findViewById(R.id.LVAgenda);
arrayListAgenda = new ArrayList<String>();
arrayAdapterAgenda = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, arrayListAgenda);
LVAgenda.setAdapter(arrayAdapterAgenda);
}
public void DoneClick(View v){
EditText aTask = (EditText)findViewById(R.id.aTask);
String agenda = aTask.getText().toString().trim();
if(agenda.isEmpty()){
return;
}
arrayAdapterAgenda.add(agenda);
aTask.setText("Add task");
}
As commonsware said you can use getView() of ArrayAdapter to do this.
I have implemented Facebook friend selector with ListAdapter. I will share the code. May be it helps. Please try.
First make a XML file in layout that defines the layout of each item of your 'to do list'.
In my case it is a facebook profile image and a checked textbox. (There is also a spacer for alignment)
Facebook.xml
<?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="wrap_content"
android:orientation="vertical">
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<ImageView
android:id="#+id/img"
android:layout_width="50dp"
android:layout_height="50dp"
android:paddingLeft="10dp"/>
<CheckedTextView
android:id="#+id/name"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center_vertical|right"
android:paddingLeft="10dp"
android:paddingRight="10dp"
android:checkMark="?android:attr/listChoiceIndicatorMultiple"
android:textColor="#android:color/black"
android:textStyle="bold" />
</LinearLayout>
<View
android:id="#+id/spacer"
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="#android:color/white"/>
</LinearLayout>
Now prepare your data array. In my case it is custom class array where each element contains a facebook name, profilepic, and a boolean.
public class Item{
public final String text;
public final Drawable icon;
public boolean isChecked;
public Item(String text, Drawable icon, boolean ischeck) {
this.text = text;
this.icon = icon;
this.isChecked = ischeck;
}
#Override
public String toString() {
return text;
}
}
I call the below code by passing friendsArray from another activity.
final Item[] items = new Item[friendsArray.length];
try {
int a = 1;
for (int i = 0; i < friendsArray.length; i++) {
tsk = new DownloadImageTask();
Bitmap bmp = (Bitmap) tsk.execute(new RaceActivity.FriendInfo[]{friendsArray[i]}).get();
Resources res = getActivity().getResources();
drawable = new BitmapDrawable(res, bmp);
items[i] = new Item(friendsArray[i].name, drawable,false);
}
}
catch(Exception ex)
{
}
Now your data array is prepared. You can pass this to ListAdapter(items in my case).
Its nice to understand the working of a List adapter. I created a scrollable List. What this logic does is it reuses the Views while scrolling.
ListAdapter adapter = new ArrayAdapter<Item>(
getActivity(),
android.R.layout.select_dialog_item,
android.R.id.text1,
items){
public View getView(int position, View convertView, ViewGroup parent) {
View v = convertView;
FaceBookHolder fb = new FaceBookHolder();
if(convertView == null)
{
LayoutInflater inflater = (LayoutInflater) getActivity().getApplicationContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
v = inflater.inflate(R.layout.facebook,null);
fb.Name = (CheckedTextView) v.findViewById(R.id.name);
fb.img = (ImageView) v.findViewById(R.id.img);
fb.spacer = (View) v.findViewById(R.id.spacer);
fb.Name.setOnClickListener(new View.OnClickListener()
{
#Override
public void onClick(View v)
{
CheckedTextView cv = (CheckedTextView)v;
if(cv.isChecked())
cv.setChecked(false);
else
cv.setChecked(true);
for(int i =0;i< items.length; i++)
{
if(items[i].text == cv.getText())
{
items[i].isChecked = cv.isChecked();
}
}
}
});
v.setTag(fb);
}
else
fb = (FaceBookHolder) v.getTag();
Item itm = items[position];
fb.Name.setText(itm.text);
fb.img.setImageDrawable(itm.icon);
fb.Name.setChecked(itm.isChecked);
return v;
}
};
you get the view in getView(), so you can modify it however you want.(change color , font etc)
Hope it helps! cheers!

ListView adapter screws up item heights

I have a custom BaseAdapter used for a ListView. The usual layout of a row looks like on the first picture.
Gallery is here
But the list can also have items whose second line's string is empty, like on the second picture, and if that second line's string has a length equal to 0, the second line's view's setVisibility(View.GONE) method is called.
And when the list is filled with items so it's neccessary to scroll to see the most-bottom items, and you scroll down and there is an item with only one line, and you scroll back to top, some of the two-line items can lose their second line, like on the third picture.
A simillar thing happens when an item is deleted from the list - the item going on its place gets the height of the deleted one - like on the fourth picture (forget the colored bar).
So it seems that the adapter thinks the "Cookies" item is the same as the "Something" item... or something.
Why does it happen? How can i fix that?
Another gallery to show exactly what happens
Adapter code:
public class CounterItemAdapter extends BaseAdapter{
private Activity activity;
private ArrayList<CounterItem> data;
private SQLiteOpenHelper helper;
private static LayoutInflater inflater = null;
public CounterItemAdapter(Activity activity, ArrayList<CounterItem> data, SQLiteOpenHelper helper) {
this.activity = activity;
this.data = data;
this.helper = helper;
inflater = (LayoutInflater)activity.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
}
#Override
public int getCount() {
return data.size();
}
#Override
public CounterItem getItem(int position) {
return data.get(position);
}
#Override
public long getItemId(int position) {
return getItem(position).getId();
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View view = convertView;
if(convertView == null)
view = inflater.inflate(R.layout.counter_list_item, null);
TextView nameView = (TextView)view.findViewById(R.id.nameView);
TextView descView = (TextView)view.findViewById(R.id.descView);
final TextView countView = (TextView)view.findViewById(R.id.countView);
ImageButton plusButton = (ImageButton)view.findViewById(R.id.plusButton);
final CounterItem counterItem;
counterItem = data.get(position);
nameView.setText(counterItem.getName());
if(counterItem.getDesc().length() == 0){
descView.setVisibility(View.GONE);
Log.d(HomeActivity.DEBUG_TAG, "GONE " + counterItem.getName() + ", LENGTH " + counterItem.getDesc().length());
}else
descView.setText(counterItem.getDesc());
countView.setText(counterItem.getCount() + "");
plusButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
counterItem.increment(helper.getWritableDatabase());
countView.setText(counterItem.getCount() + "");
}
});
View categoryView = view.findViewById(R.id.category);
String colors[] = {"#ff99cc00",
"#ff00ddff",
"#ffffbb33",
"#ffaa66cc",
"#ffcc0000"};
Random rand = new Random();
String color = colors[rand.nextInt(colors.length)];
categoryView.setBackgroundColor(Color.parseColor(color));
return view;
}
}
Row layout:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:baselineAligned="false"
android:descendantFocusability="blocksDescendants"
>
<LinearLayout
android:orientation="vertical"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_toLeftOf="#+id/linearLayout" android:layout_centerVertical="true"
android:gravity="center_vertical" android:paddingLeft="12dp" android:paddingRight="12dp"
android:paddingBottom="8dp" android:paddingTop="8dp" android:id="#+id/linearLayout1">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Counter name"
android:id="#+id/nameView"
android:textSize="16dp"/>
<TextView android:layout_width="wrap_content" android:layout_height="wrap_content"
android:id="#+id/descView"
android:textSize="16dp"
android:textColor="#color/dividerGrey"
android:text="wtf"/>
</LinearLayout>
<LinearLayout
android:orientation="horizontal"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:id="#+id/linearLayout" android:gravity="center_vertical" android:layout_centerVertical="true"
android:paddingTop="8dp" android:paddingBottom="8dp">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="13"
android:id="#+id/countView"
android:textSize="30dp"
android:layout_marginRight="13dp" android:textColor="#color/dividerGrey"/>
<View android:layout_width="0.5dp" android:layout_height="48dp"
android:background="#color/dividerGrey" android:id="#+id/plusDivider"/>
<ImageButton
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="#+id/plusButton"
android:src="#drawable/ic_button_increment"
style="#android:style/Widget.Holo.Light.ActionButton"
android:contentDescription="#string/plus_button"/>
</LinearLayout>
</RelativeLayout>
Well i suppose you use the ViewHolder pattern in a ListView right?
I don't want to try and explain the whole usage of this pattern as there are many other good tutorials.
So the case you describe happens, because the listAdapter reuses the convertView that was inflated for every object so if the last object for your listview has setVisibility(View.Gone) for your second line then when you scroll up all the other childs that become visible will also have visibility(View.Gone).
A simple workaround for this is to setVisibility for each row
like this:
if (convertView == null) {
convertView = (View) inflater.inflate(R.layout.view_item, null);
viewHolder = new ViewHolder();
viewHolder.itemName = (TextView) convertView.findViewById(R.id.item_name);
viewHolder.secondLine = (TextView) convertView.findViewById(R.id.second_line)
convertView.setTag(viewHolder);
}
else {
viewHolder = (ViewHolder) convertView.getTag();
}
if(<-- your condition here-->){
secondLine.setVisibility(View.VISIBLE);
}
else{
secondLine.setVisibility(View.GONE);
}
And now that you have added the code for your adapter i believe that your mistake is here:
if(counterItem.getDesc().length() == 0){
descView.setVisibility(View.GONE);
Log.d(HomeActivity.DEBUG_TAG, "GONE " + counterItem.getName() + ", LENGTH " + counterItem.getDesc().length());
}else {
**descView.setVisibility(View.VISIBLE);**
descView.setText(counterItem.getDesc());
}
You should add code to set it visible again if needed bacause with your code when it gets gone for one of the rows it stays gone forever.
Update
Also for your second problem, whenever you delete an item from your listview you should remove the item from the adapter and call notifyDataSetChanged(); in order for the adapter to recreate the Views.

Populating ListView from Cursor

I've been looking on here for quite some time and I cannot find an answer to my problem. There are many similar issues. But I haven't been able to come up with any solution that works. I am very new to android/java programming so I really appreciate any help.
Background: I have an activity screen that contains 2 listviews. The first listview contains a list of names coming from a database. (This is working correctly).
Then when a user clicks on one of the names in the list, the onItemClick event fires and builds a cursor from all the data associated from the name. (Have verified that I am getting a cursor with valid results).
Problem: When I try to put the resulting cursor into a SimpleCursorAdapter to set my listadpter to, I am receiving no errors, but I'm not getting anything displayed to the screen.
I have data coming from a SQLite database. It is simply 4 columms with X number of rows. All I want to do is display everything on the list and then maybe do some analysis.
Here is my code:
public class TipCalculator_w_Database extends Activity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.databaseinfoscreen);
ListView lv1 = null;
lv1 = (ListView) findViewById (R.id.List1);
final ListView lv2;
lv2 = (ListView) findViewById (R.id.List2);
final DataBase showData = new DataBase(getApplicationContext());
showData.open();
Cursor NameList = showData.GetAllNames();
startManagingCursor(NameList);
// Create an ArrayAdapter, that will actually make the Strings above
// appear in the ListView
int i = NameList.getCount();
String[] FinalString = new String[i];
int j = 0;
while (NameList.moveToNext()) {
FinalString[j] = NameList.getString(0);
j++;
}
if(j != 0)
{
lv1.setAdapter(new ArrayAdapter<String>(this, android.R.layout.simple_gallery_item, FinalString));
}
NameList.close();
showData.close();
lv1.setOnItemClickListener(new OnItemClickListener() {
public void onItemClick(AdapterView<?> parent, View view,
int position, long id) {
String selectedName = ((TextView) view).getText().toString();
Context ctx= parent.getContext();
final DataBase showData = new DataBase(getApplicationContext());
showData.open();
final String[] columns = new String[] { showData.KEY_ROWID, showData.KEY_NAME, showData.KEY_BILL, showData.KEY_TIP};
final int[] to = new int[] { R.id.ID_list, R.id.Name_list, R.id.Bill_list, R.id.Tip_list};
Cursor NewEntry = showData.GetRowByName(selectedName);
startManagingCursor(NewEntry);
lv2.setAdapter(new SimpleCursorAdapter(ctx, R.layout.listlayout, NewEntry, columns, to));
showData.close();
NewEntry.close();
// Back Button Functionality
Button backButton = (Button) findViewById(R.id.Back);
backButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
Intent myIntent = new Intent(v.getContext(), Tipscreen.class);
startActivityForResult(myIntent, 0);
}
}
);
}
}
Here is my XML Files
listlayout.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="wrap_content" >
<TextView
android:id="#+id/ID_list"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="10px" />
<TextView
android:id="#+id/Name_list"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="10px" />
<TextView
android:id="#+id/Bill_list"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="10px" />
<TextView
android:id="#+id/Tip_list"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="10px"/> </LinearLayout>
-- databaseinfoscreen.xml --
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<LinearLayout
android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_weight="1">
<ListView android:text="List" android:id="#+id/List1" android:layout_width="wrap_content" android:textSize="18px" android:layout_height="fill_parent">
</ListView>
</LinearLayout>
<LinearLayout
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_weight="1">
<ListView android:text="List" android:id="#+id/List2" android:layout_width="wrap_content" android:textSize="18px" android:layout_height="fill_parent">
</ListView>
</LinearLayout>
<RelativeLayout
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_weight="1">
<Button android:text="#string/back" android:layout_alignParentBottom="true" android:id="#+id/Back" android:layout_width="wrap_content" android:textSize="18px" android:layout_height="40px"></Button>
</RelativeLayout>
</LinearLayout>
Hopefully this is enough to go off of, if there is any more information needed please let me know. I am getting all the information I need in the cursor it just isn't being displayed. I am open to alternate means of doing this. However, I cannot make this particular class extend ListActivity since it needs to be extending activity.
Also of note. I have been able to display to that listview using the following line of code. Unfortunately this is only displays one column rather than all columns which I want.
lv2.setAdapter(new ArrayAdapter<String>(ctx, android.R.layout.simple_gallery_item, FinalString));
Where FinalString is simply a full column of data from the database that was manipulated into an array.
Thanks for the help.
Well since no one responded, I figured I'd answer this myself.
Here are the steps, I've got this working for a different project so I'll summarize. I can go into more detail if anyone needs it.
XML:
<ListView android:text="List" android:id="#+id/android:list" android:layout_width="wrap_content" android:scrollbars="horizontal" android:textSize="18px" android:layout_height="fill_parent" android:minHeight="120px">
</ListView>
Step 1: Create a Cursor and populate using DB functions (not shown)
String dbCount = showData.getTotalCount();
Cursor GetAllData = showData.GetAllData();
startManagingCursor(GetAllData);
Step 2: Create an ArrayList that comprises a bunch of get values and fill the list with items from the cursor.
int i = GetAllData.getCount();
ArrayList<DB_Get_Values> dbList = new ArrayList<DB_Get_Values>();
while (GetAllData.moveToNext()) {
DB_Get_Values w = new DB_Get_Values(GetAllData.getString(0), GetAllData.getString(1), GetAllData.getString(2));
dbList.add( w );
j++;
}
Step 3: Create an adapter class that will be used to control the display of the listview
DBAdapter T_Adapter = new DBAdapter(
this,
dbList );
setListAdapter( T_Adapter );
GetAllData.close();
DB_Get_Values Class: Full of Getters
public class DB_Get_Values {
public String P_Key = "";
public String Mac_addr = "";
public String SDK_VRS = "";
public DB_Get_Values( String P_Key, String Mac_addr, String SDK_VRS)
{
this.P_Key = P_Key;
this.Mac_addr = Mac_addr;
this.SDK_VRS = SDK_VRS;
}
public String get_Mac_addr() {
return Mac_addr;
}
public String get_P_Key() {
return P_Key;
}
public String get_SDK_VRS() {
return SDK_VRS;
}
}
Class DBAdapter: Get's the values to put into the listview and allows for programatically controlling the display.
class AdapterView extends LinearLayout {
public AdapterView(Context context,
DB_Get_Values list ) {
super( context );
this.setOrientation(HORIZONTAL);
LinearLayout.LayoutParams PkeyParams =
new LinearLayout.LayoutParams(40, LayoutParams.WRAP_CONTENT);
PkeyParams.setMargins(1, 1, 1, 1);
TextView Pkey = new TextView( context );
Pkey.setText( list.get_P_Key() );
Pkey.setTextSize(14f);
Pkey.setTextColor(Color.WHITE);
addView( Pkey, PkeyParams);
LinearLayout.LayoutParams MacAddrParams =
new LinearLayout.LayoutParams(100, LayoutParams.WRAP_CONTENT);
MacAddrParams.setMargins(1, 1, 1, 1);
TextView MacAddr = new TextView( context );
MacAddr.setText( list.get_Mac_addr() );
MacAddr.setTextSize(14f);
MacAddr.setTextColor(Color.WHITE);
addView( MacAddr, MacAddrParams);
LinearLayout.LayoutParams SDK_VRSParams =
new LinearLayout.LayoutParams(20, LayoutParams.WRAP_CONTENT);
SDK_VRSParams.setMargins(1, 1, 1, 1);
TextView SDK_VRS = new TextView( context );
SDK_VRS.setText( list.get_SDK_VRS() );
SDK_VRS.setTextSize(14f);
SDK_VRS.setTextColor(Color.WHITE);
addView( SDK_VRS, SDK_VRSParams);
}
}
public class DBAdapter extends BaseAdapter {
private Context context;
private List<DB_Get_Values> list;
public DBAdapter(Context context, List<DB_Get_Values> list ) {
this.context = context;
this.list = list;
}
public int getCount() {
return list.size();
}
public Object getItem(int position) {
return list.get(position);
}
public long getItemId(int position) {
return position;
}
}
Step 5: You are now done! Change the parameters in AdapterView class to modify how it looks. Hopefully this helps someone.

Categories

Resources