Handling images in HTML In TextView in a different way - android

I'm trying to show Html in TextView. And all works fine, but I want to display images in TextView in different way.
For example, I want each image to be in the container, which can be dragged and dropped.
Does anyone knows any way to implement this?
Thanks!
Upd: I can't use WebView, because I will show about 10-20 separate views on same Activity at once. And I don't think it will help me to implement this.

This is how I solved the problem.
The main idea:
Go through all spans from Html.fromHtml(section.text) and search for ImageSpan. Once it is found set all previous spans as text for one TextView, create ImageView for image, continue searching.
Code:
new AsyncTask<String, Integer, List<Object>>() {
#Override
protected List<Object> doInBackground(String... params) {
List<Object> res = new ArrayList<Object>();
Spanned in = Html.fromHtml(section.text);
Object[] spans = in.getSpans(0, Integer.MAX_VALUE, Object.class); // get all spans
int lastImageSpanPosition = 0; // it's end position of image span
for (int i = 0; i < spans.length; i++) { // itarete searching ImageSpan
Object span = spans[i];
if (span instanceof ImageSpan) {
int spanStart = in.getSpanStart(span); // If you;ve found one
if (lastImageSpanPosition == spanStart)
continue; // check if image is first span (avoid creation of empty spans).
res.add(new SpannableStringBuilder(in.subSequence(lastImageSpanPosition, spanStart))); // add all previous spans as a single Spannable object
ImageSpan imageSpan = (ImageSpan) span;
String imageUrl = imageSpan.getSource();
if (!imageUrl.startsWith("http"))
imageUrl = "http:" + imageUrl;
res.add(new ImageSpan(null, imageUrl)); // add separate span for image
lastImageSpanPosition = in.getSpanEnd(span);
}
if (i < spans.length - 1 && !containsImageSpan(spans, i + 1)) { // to not lose text in the end
res.add(new SpannableStringBuilder(in.subSequence(lastImageSpanPosition, in.getSpanEnd(spans[spans.length - 1]))));
break;
}
}
return res;
}
#Override
protected void onPostExecute(List<Object> objects) {
for (Object object : objects) {
View v = null;
if (object instanceof ImageSpan) { // create separate views for each span
NetworkImageView networkImageView = new NetworkImageView(getContext());
networkImageView.setImageUrl(((ImageSpan) object).getSource(), App.get().getImageLoader());
v = networkImageView;
} else {
TextView textView = new TextView(getContext());
textView.setText((CharSequence) object);
v = textView;
}
holder.sectionTextViewsContainer.addView(v);
}
}
}.execute(section.text);
EDIT: added containsImageSpan method
private boolean containsImageSpan(Object[] spans, int index) {
for (int i = index; i < spans.length; i++) {
if (spans[i] instanceof ImageSpan) {
return true;
}
}
return false;
}

According to the documentation of HTML class, I'm not sure you can achieve what you want. Take a look at
http://developer.android.com/reference/android/text/Html.html
If you really want to use HTML I recommand you to use a webview. Otherwise, you can use native drag and drop. Check the official training: http://developer.android.com/guide/topics/ui/drag-drop.html

Related

How to get text from dynamically created TextView in android?

private void textview(String question) {
TextView clientAddress = new TextView(this);
clientName.setGravity(1);
clientAddress.setTypeface(Typeface.defaultFromStyle(1));
clientAddress.setText(question);
clientAddress.setLayoutParams(lparams);
linearLayout.addView(clientAddress);
clientAddress.setTextSize(20);
clientAddress.setTextColor(getResources().getColor(R.color.white));
clientAddress.setPadding(7, 10, 0, 10);
}
I am calling this function to create 6 TextViews dynamically. but i am confused , how to get text from these TextViews. can anyone suggest me the solution?
You have to create a different object for each and every TextView. and after using those object you can get values of TextView. using getText() property of TextView.
You can also create array of TextView.
Do something like this
private void textview(int position, String question) {
TextView clientAddress = new TextView(this);
clientName.setGravity(1);
clientAddress.setTypeface(Typeface.defaultFromStyle(1));
clientAddress.setText(question);
clientAddress.setTag(position);
clientAddress.setLayoutParams(lparams);
linearLayout.addView(clientAddress);
clientAddress.setTextSize(20);
clientAddress.setTextColor(getResources().getColor(R.color.white));
clientAddress.setPadding(7, 10, 0, 10);
}
public String getTextFromTextView(int positionOfTextView , LinearLayout mLinearLayout){
String result = "";
for (int i = 0; i <((ViewGroup)mLinearLayout).getChildCount() ; i++) {
if(((ViewGroup) mLinearLayout).getChildAt(i)!=null ){
View mView =((ViewGroup) mLinearLayout).getChildAt(i);
if(mView instanceof TextView){
TextView mTextView = (TextView)mView;
if(mTextView!=null && mTextView.getTag()!=null && positionOfTextView==(int) mTextView.getTag() ){
result =mTextView.getText().toString();
}
}else {
TextView mTextView = (TextView) mView.findViewWithTag(positionOfTextView);
if(mTextView!=null){
result =mTextView.getText().toString();
}
}
}
}
return result;
}
you can use the gettext() method to get the text from the text view. and if you wants to convert the fetched text into string then you can use tostring() .
String fetchedtext = clientAddress.getText().toString();

Java, what is best way to compare two lists?

I have two lists of Default and Chrome browsers history.
I want to merge these two lists into one list.
I need to update item if I find it duplicate (is common between two lists).
So, my "BrowserRecord" class is like this:
public class BrowserRecord {
private long id;
private int bookmark;
private long created;
private long date;
private String title;
private String url;
private long visits;
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
BrowserRecord record = (BrowserRecord) o;
return url.equals(record.url);
}
#Override
public int hashCode() {
return url.hashCode();
}
// other getter setter methods
...
}
and finally, I have a method that gets browsers histories and does merging:
public List<BrowserRecord> getHistory() {
List<BrowserRecord> browserList = new ArrayList<BrowserRecord>();
// get history of default and chrome browsers
List<BrowserRecord> defaultList = getDefaultBrowserHistory();
List<BrowserRecord> chromeList = getChromeBrowserHistory();
Log.e(TAG, "=> size of Default browser:" + defaultList.size());
Log.e(TAG, "=> size of Chrome browser:" + chromeList.size());
// compare list A with B, update A item if equal item found in B and push it to tempList
for(int i=0; i<chromeList.size(); i++) {
BrowserRecord chromeBrowser = chromeList.get(i);
for(int j=0; j<defaultList.size(); j++) {
BrowserRecord defaultBrowser = defaultList.get(j);
if(chromeBrowser.equals(defaultBrowser)) {
if(chromeBrowser.getBookmark() != defaultBrowser.getBookmark())
chromeBrowser.setBookmark(1);
chromeBrowser.setVisits(chromeBrowser.getVisits() + defaultBrowser.getVisits());
}
}
browserList.add(chromeBrowser);
}
// compare list B with A, jump if equal item found in A, push to tempList if item not found
for(int i=0; i<defaultList.size(); i++) {
BrowserRecord defaultBrowser = defaultList.get(i);
boolean found = false;
for(int j=0; j<chromeList.size(); j++) {
BrowserRecord chromeBrowser = chromeList.get(j);
if(defaultBrowser.equals(chromeBrowser)) {
found = true;
break;
}
}
if(!found)
browserList.add(defaultBrowser);
}
Log.e(TAG, "=> size of final browser:" + browserList.size());
return browserList;
}
I have tested this method and is working fine. Since my history records on mobile device after 3 years didn't exceed more than 200 records on one list and 150 for others, I assume something similar is happening for other users. But I'm sure is not optimum way.
What do you recommend?
any suggestion would be appreciated. Thanks.
Not sure I understand correctly, but it seems like what you're trying to do is, given both lists, create a final list which will contain all of the elements from both lists, removing any duplicates.
If this is the case, then take a look at Java's TreeSet class. If you iterate over all of the elements from both your lists and insert them into a TreeSet, you will basically get the result you're looking for. You can then use an Iterator to create an ArrayList containing all of the non-duplicate items from both your lists. As a side-effect of using a TreeSet, they will ordered (you can also use either a HashSet if you don't care about the order or a LinkedHashSet if you want to preserve the order of insertion).

Android update array of bitmaps / Queue

I need some advice how to implement this situation in my application.
I have array of bitmpaps, which I'm using to store different states of my Canvas, so I can use them in the future. Here is the code which I'm using :
private Bitmap[] temp;
// on user click happens this ->
if(index<5){
temp[index] = Bitmap.createBitmap(mBitmap);
index++;
}
So basically I want to save only the last 5 bitmaps depending on user's actions. The things which I want to learn is how can I update my array so I can always have the last 5 bitmaps.
Here is what I mean :
Bitmaps [1,2,3,4,5] -> after user clicks I want to delete the first bitmap, re-order the array and save the new one as the last..so my array should look like this : Bitmaps[2,3,4,5,6];
Any suggestions / advices which is the best way to do that?
Thanks in advance!
I just wrote this...
Use this code to initialise:
Cacher cach = new Cacher(5);
//when you want to add a bitmap
cach.add(yourBitmap);
//get the i'th bitmap using
cach.get(yourIndex);
Remember you can re implement the function get to return the ith "old" Bitmap
public class Cacher {
public Cacher(int max) {
this.max = max;
temp = new Bitmap[max];
time = new long[max];
for(int i=0;i<max;i++)
time[i] = -1;
}
private Bitmap[] temp;
private long[] time;
private int max = 5;
public void add(Bitmap mBitmap) {
int index = getIndexForNew();
temp[index] = Bitmap.createBitmap(mBitmap);
}
public Bitmap get(int i) {
if(time[i] == -1)
return null;
else
return temp[i];
}
private int getIndexForNew() {
int minimum = 0;
long value = time[minimum];
for(int i=0;i<max;i++) {
if(time[i]==-1)
return i;
else {
if(time[i]<value) {
minimum = i;
value = time[minimum];
}
}
return minimum;
}
}

Android App- Using a loop for setText procedure to populate EditText

Trying to use a loop to set the text of 12 checkboxes from a db query. Would like to substitute "add1" with an array value and loop through all 12 instead of spelling out each one. Any ideas of how to do this?
Here is the code I am trying to modify:
add1Text= (CheckBox) findViewById(R.id.add1);
if (cursor.getString(cursor.getColumnIndex("add1")) == null) {
add1Text.setVisibility(View.GONE);
}
else {
add1Text.setText(cursor.getString(cursor.getColumnIndex("add1")));
}
Please note: everything below is off the top of my head, I can't test it right now. I'll test it later when I get a chance.
I think you'll need to keep track of which column to associate with each CheckBox... I'm presuming it's something like this:
Column: add1 => Checkbox: add1Text
Column: add2 => Checkbox: add2Text
and so on and so forth.
In this circumstance, you'll need to manually keep track of them, possibly in an array. I'd suggest making a Pair class that you can use. I've altered the class from this post [ A Java collection of value pairs? (tuples?) ]
public class Pair<L,R> {
private final L left;
private final R right;
public Pair(L left, R right) {
this.left = left;
this.right = right;
}
public L getLeft() { return left; }
public R getRight() { return right; }
#Override
public int hashCode() { return left.hashCode() ^ right.hashCode(); }
#Override
public boolean equals(Object o) {
if (o == null) return false;
if (!(o instanceof Pair)) return false;
Pair pairo = (Pair) o;
return this.left.equals(pairo.getLeft()) &&
this.right.equals(pairo.getRight());
}
}
Now, you'll need to make a List (or similar) containing the pairs that you want.
List<Pair<CheckBox, String>> list = new ArrayList<Pair<CheckBox, String>>;
list.add(new Pair<CheckBox, String>((CheckBox) findViewById(R.id.add1), "add1");
list.add(new Pair<CheckBox, String>((CheckBox) findViewById(R.id.add2), "add2");
list.add(new Pair<CheckBox, String>((CheckBox) findViewById(R.id.add3), "add3");
and so on and so forth
Then you can iterate through the List using something like
foreach (Pair<CheckBox, String> item in list)
{
if (cursor.getString(cursor.getColumnIndex(item.getLeft()) == null)
{
item.getRight().setVisibility(View.GONE);
}
else
{
item.getRight().setText(cursor.getString(cursor.getColumnIndex(item.getLeft()));
}
}
Got it! Forgot that I was dealing with objects and also realized I needed a third array. Here is what I came up with.
cList contains column names
fList are the objects (in this case CheckBoxes)
pList are the names of the objects I am selecting from the layout.
Object fList[]={add1Text,add2Text,add3Text};
int pList[]={R.id.add1,R.id.add2,R.id.add3};
cList = cursor.getColumnNames();
for (int i =0; i < fList.length; i++){
fList[i] = (CheckBox) findViewById(pList[i]);
if (cursor.getString(cursor.getColumnIndex(cList[i])) == null) {
((TextView) fList[i]).setVisibility(View.GONE);
}
else {
((TextView) fList[i]).setText(cList[i] + " - " + cursor.getString(cursor.getColumnIndex( cList[i])));
}
}
Sets the CheckBox text to ( Column name - Value )

How to delete the old lines of a TextView

I'm developping an app which constantly needs to show the results to the user in a TextView like some sort of log.
The app works nicely and it shows the results in the TextView but as long as it keeps running and adding lines the app gets slower and crashes because of the character length of the TextView.
I would like to know if the android API provides any way to force a TexView to automatically delete the oldest lines that were introduced in order to make room for the new ones.
I had the same problem. I just resolved it.
The trick is to use the getEditableText() method of TextView. It has a replace() method, even a delete() one. As you append lines in it, the TextView is already marked as "editable", which is needed to use getEditableText(). I have something like that:
private final static int MAX_LINE = 50;
private TextView _debugTextView; // Of course, must be filled with your TextView
public void writeTerminal(String data) {
_debugTextView.append(data);
// Erase excessive lines
int excessLineNumber = _debugTextView.getLineCount() - MAX_LINE;
if (excessLineNumber > 0) {
int eolIndex = -1;
CharSequence charSequence = _debugTextView.getText();
for(int i=0; i<excessLineNumber; i++) {
do {
eolIndex++;
} while(eolIndex < charSequence.length() && charSequence.charAt(eolIndex) != '\n');
}
if (eolIndex < charSequence.length()) {
_debugTextView.getEditableText().delete(0, eolIndex+1);
}
else {
_debugTextView.setText("");
}
}
}
The thing is, TextView.getLineCount() returns the number of wrapped lines, and not the number of "\n" in the text... It is why I clear the whole text if I reach the end of the text while seeking the lines to delete.
You can do that differently by erasing a number of characters instead of erasing a number of lines.
This solution keeps track of the log lines in a list and overwrites the textview with the contents of the list on each change.
private List<String> errorLog = new ArrayList<String>();
private static final int MAX_ERROR_LINES = 70;
private TextView logTextView;
public void addToLog(String str) {
if (str.length() > 0) {
errorLog.add( str) ;
}
// remove the first line if log is too large
if (errorLog.size() >= MAX_ERROR_LINES) {
errorLog.remove(0);
}
updateLog();
}
private void updateLog() {
String log = "";
for (String str : errorLog) {
log += str + "\n";
}
logTextView.setText(log);
}
Here is an example that adds lines to an output log limited by the set max lines. The scrollview will auto scroll to the bottom after every line is added. This example work purely with the contents of the TextView so it doesn't have the need for a separate data collection.
Add the following to your activity xml:
<ScrollView
android:id="#+id/scrollView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:scrollbars="vertical" >
<TextView
android:id="#+id/textViewOutput"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:maxLines="1000" />
</ScrollView>
In your activity add the following code:
private static final int MAX_OUTPUT_LINES = 50;
private static final boolean AUTO_SCROLL_BOTTOM = true;
private TextView _textViewOutput;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
_textViewOutput = (TextView) findViewById(R.id.textViewOutput);
}
//call to add line(s) to TextView
//This should work if either lineText contains multiple
//linefeeds or none at all
private void addLinesToTextView(String lineText) {
_textViewOutput.append(lineText);
removeLinesFromTextView();
if(AUTO_SCROLL_BOTTOM)
_scrollView.post(new Runnable() {
#Override
public void run() {
_scrollView.fullScroll(ScrollView.FOCUS_DOWN);
}
});
}
// remove leading lines from beginning of the output view
private void removeLinesFromTextView() {
int linesToRemove = _textViewOutput.getLineCount() - MAX_OUTPUT_LINES;
if (linesToRemove > 0) {
for (int i = 0; i < linesToRemove; i++) {
Editable text = _textViewOutput.getEditableText();
int lineStart = _textViewOutput.getLayout().getLineStart(0);
int lineEnd = _textViewOutput.getLayout().getLineEnd(0);
text.delete(lineStart, lineEnd);
}
}
}
The TextView shows what you set via setText() method. So this sounds to me like you should cut down the input you provide.
To empty the TextView, you can do setText("");
Kotlin answer of Vincent Hiribarren
fun write_terminal_with_limit(data: String?, limit:Int)
{
log_textView.append(data)
val nb_line_to_del: Int = log_textView.lineCount - limit
// Erase excessive lines
if (nb_line_to_del > 0)
{
var end_of_line_idx = -1
val char_seq: CharSequence = log_textView.text
for (i in 0 until nb_line_to_del)
{
do
{
end_of_line_idx++
}
while (end_of_line_idx < char_seq.length && char_seq[end_of_line_idx] != '\n')
}
if (end_of_line_idx < char_seq.length)
{
log_textView.editableText.delete(0, end_of_line_idx + 1)
}
else
{
log_textView.text = ""
}
}
}
I made personnal adjustment...
I think you are using TextView.append(string) then it will add to old text.
If you are setting using setText it will replace the old text
This is an old one, but I just found looking for a solution to my own problem.
I was able to remove all TextViews from a LinearLayout using nameoflayout.removeAllViews();
There is another method that will allow you to remove views from specified places in the layout using ints, it's: nameoflayout.removeViews(start, count); so I'm sure you could create a time out for how long textviews remain visible.
No, android API doesn't provide any functionally that delete oldest lines from textview automatically till API level 25. you need to do it logically.
Try to write a function that takes an old string on TextView and add new string to it, then get substring last strings that TextView capable. And set it to TextView. Something like this:
String str = textview.getText();
str += newstring;
int ln = str.length();
ln = ln-250;
if (ln<0) ln=0;
str = str.substring(ln);
textview.setText(str);
reference Vincent Hiribarren answer.
make it simple-->
TextView _debugTextView;
//if excess 20 lines keep new 200 chars
if(_debugTextView.getLineCount() >20) _debugTextView.getEditableText().delete(0,_debugTextView.getText().length()-200);

Categories

Resources