Auto-scrolling textview in Android - android

I've tried a lot of different ways, most of the suggestions found here, but none of them seems to work. What I'm trying to achieve is at chat area below my game area, a SurfaceView. It is supposed to scroll upwards as new lines are added to the textview.
At first, it looks like a really simple task, but having tried all kinds of suggestions, like a TextView in a ScrollView, like a TextView in a TableRow in a TableLayout in a ScrollView, and so on...I've still not made it happen. Of course this must be something easily achieved in Android, right??
The task is to display like 6 lines of text in the bottom of the screen, and as a new message is added last it should scroll the rest upwards, like a terminal window. The important thing is that it should add the latest message after the other and, when reached the bottom line, scroll the text upwards and add the new line(s) at the end.
Any kind of help or suggestions would be highly appreciated!!

I needed the same behavior in one of my apps and I achieved in just with one command:
view.setGravity(Gravity.BOTTOM);
Or, analogously, setting this attribute in your layout:
android:gravity="bottom"
Then simply add your lines using:
your_text_view.append(newLine);

Suppose, you declared your ScrollView as follows...
private ScrollView mScrollView;
you initialized it as...
mScrollView = (ScrollView) findViewById(R.id.scroll_view_chat_window);
Now, create a method to perform scroll down when you call the method. Inside the method implement a thread which will do the scroll down independently. And call the method after every chat message update thats will do the auto-srcoll functionality.
private void scrollDown() {
mScrollView.post(new Runnable() {
#Override
public void run() {
mScrollView.smoothScrollTo(mScrollView.getScrollY(), mScrollView.getScrollY()
+ mScrollView.getHeight());
}
});
}

I've achieved this (crudely!) by maintaining my own list, deleting the lowest element then adding at the end each time. Here i've just got a 3 line window:
public class MessageWindow {
private ArrayList <String> msgs;
private Activity parentActivity;
public MessageWindow(Activity act, int allMsgsMax) {
this.parentActivity = act;
msgs = new ArrayList <String> ();
// create empty list elements for initial display
for (int i = 0; i < allMsgsMax; i++){
msgs.add("");
}
}
//
public void put (String msg){
msgs.remove(0);
msgs.add(msg);
// get a handle to the textview 'messages', a 3-line box
TextView t2v = (TextView) parentActivity.findViewById(R.id.messages);
// crappy but you get the idea:
t2v.setText(msgs.get(0) + "\n" + msgs.get(1) + "\n" + msgs.get(2) );
}
then in the activity:
protected MessageWindow messageWindow;
// setup splash screen
messageWindow = new MessageWindow(this, 3);
// write some stuff - row1 will disappear off the top of the box
messageWindow.put ("row1")
messageWindow.put ("row2")
messageWindow.put ("row3")
messageWindow.put ("row4")

Related

Setting Text Based on values held in ArrayList

This is driving me a little mad since I know this should be very simple but I am not getting the desired affect.
I have the following arraylist
private List<String> tagStringArray = new ArrayList<>();
Then later I have a method that creates dynamic buttons, based on ID values pulled across from my Retrofit instance.
In my method, I have a count to help me set the title of the button but I also add the values of count to an ArrayList for use in another method.
I have taken a snip of relevant information from the method mentioned.
count = 1;
if (!questionNumber.equals("") && !questionNumber.equals(null)) {
for (final Object value : list) {
try {
/*Dynamically create new Button which includes the question number
*/
final AppCompatButton btn_question = new AppCompatButton(getActivity());
/*LayoutParams (int width, int height,float weight)
As LayoutParams is defaulted in px, I have called a method called dpToPX to make sure
the dynamically added EditText is the same size on all devices.
*/
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(dpToPx(280), dpToPx(45), 1);
btn_question.setBackgroundColor(Color.parseColor("#3B5998"));
btn_question.setTextColor(Color.WHITE);
btn_question.setText("Question "+count);
//set the Tag based on its position in the XML
tagStringArray.add(String.valueOf((count)));
count++;
If a user clicks on say Question 1 Button, I want my fragment to say Question 1, so to try and achieve that, I have tried doing the following:
String tags = String.valueOf(tagStringArray);
tags = tags.substring(1, tags.length() -1);
String[] currentTag = tags.split(",");
if (currentTag[0].contains("1")) {
tv_setQuestions_edit.setText("Question 1");
}else if(currentTag[1].contains("2")) {
tv_setQuestions_edit.setText("Question 2");
}
But this will always set the title to Question 1 and I am not sure what is going wrong.......
If I use the following toast Toast.makeText(getActivity(), Arrays.toString(currentTag), Toast.LENGTH_LONG).show(); it shows [1,2] so I know they are being added ok.
I did look into using tags by doing:
public static int KEY_COUNT=0; public static int KEY_VALUE=1;
btn_question.setTag(KEY_VALUE,value);
btn_question.setTag(KEY_COUNT,count);
But for some reason, when I add more than one tag (as I need a minimum of 2), my dynamic button is missing from the layout. But for some reason when only 1 tag - like this btn_question.setTag(value); is used, it shows up fine (I have a feeling its some issue with my fragment). Therefore I am trying to think of a workaround in the meantime.
Any help or guidance would be really appreciated.
It's because
currentTag[0].contains("1")
is always true. The first item of currentTag always contains "1".
Instead of doing this, why don't you just do String titleForFragment = myButton.getText() in the onClick method for the button? That way, you can set the same onClickListener on all the buttons, and it will reduce the amount of code you need to write.

Remove items from array with auto generated onclick

I create a vertical list of textviews with an arraylist and attach on onclicklistener to each one. In the onclick I set code to remove that item. When I click in sequence from the last generated to the first this works fine. But if I remove the first one and then the last one it gives me a null pointer exception. I know this is happening because it is attempting to remove an index that is no longer present, or at least that is what I think is happening. But I cannot figure out how to solve that.
private void generateViews(){
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT);
final TextView[] textView = new TextView[questionArray.size()];
for(int i = 0; i < questionArray.size(); i++){
final int Index = i;
textView[Index] = new TextView(getActivity());
textView[Index].setText(questionArray.get(i));
textView[Index].setId(Index);
textView[Index].setGravity(Gravity.CENTER);
textView[Index].setPadding(15,15,15,15);
textView[Index].setLayoutParams(params);
textView[i].setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if (textView[Index].getId() == (v).getId()) {
questionArray.remove(Index);
answerArray.remove(Index);
saveVariables();
updateViews();
((ViewGroup) textView[Index].getParent()).removeView(textView[Index]);
Toast.makeText(getActivity(), "Question and Answer removed!", Toast.LENGTH_SHORT).show();
}
}
});
mainLayout.addView(textView[Index]);
}
EDIT:
I figured out a small fix but it has it's problems. Instead of removing the items from the arrays with the index I can remove them by searching for the text within the textview.
The problem with this solve is that if my array contains 2 items that are identical then it may remove the wrong index.
questionText = textView[Index].getText().toString();
answerText = textView[Index].getText().toString();
if(questionArray.contains(questionText) && questionArray.size() > 0){
questionArray.remove(questionText);
answerArray.remove(answerText);
}
Solved:
I solved it by first searching for the index of the question text and removing that index from both arrays. The arrays are user generated and I plan on preventing the user from entering the same question twice.
questionText = textView[Index].getText().toString();
int questionIndex = questionArray.indexOf(questionText);
questionArray.remove(questionIndex);
answerArray.remove(questionIndex);
Also, I did it this way because I am still an amateur and was not aware of the Recyclerview. I plan on educating myself on that function and hopefully implementing it.
I really have no idea about why you want do this? if you just want remove textview in a list , why don't you use listview or recyclerview instead ?
You should consider using RecyclerView.

Contents of textView change when contents are selectable

I am currently working on a project which requires me to dynamically add textView objects to a LinearLayout based on the contents of an array.
chapter = Home.chapters.get(index);
layout.removeAllViews();
String dataText = chapter.content;
String[] dataArray = dataText.split("~");
for (int i = 0; i < dataArray.length; i++) {
String paragraph = dataArray[i];
String outputParagraph = paragraph.replace("`", "\n");
View view = getLayoutInflater().inflate(R.layout.ttc_text, null);
final TextView tv = (TextView) view.findViewById(R.id.textView);
tv.setText(outputParagraph);
//tv.setTextIsSelectable(true);
layout.addView(view);
}
TextView titleView = (TextView) findViewById(R.id.title);
titleView.setText("Chapter " + chapter.title);
layout.setOnTouchListener(new OnSwipeTouchListener(getBaseContext()) {
public void onSwipeRight() {
retreatBackward();
}
public void onSwipeLeft() {
advanceForward();
}
});
I have configured saveOnItemStateChanged in order to restore the correct contents of the textViews when the screen is rotated.
if (savedInstanceState != null) {
index = savedInstanceState.getInt("Chapter");
}
#Override
public void onSaveInstanceState(Bundle savedInstanceState) {
savedInstanceState.putInt("Chapter", index);
super.onSaveInstanceState(savedInstanceState);
}
However, whenever the screen is rotated, the textViews present within the parent layout do not display their original contents. Instead, the same number of textViews in the original layout all display identical contents, that is, the contents of the final textView added to the layout.
This issue only presents when the text within the TextViews is designated as selectable, either through the setTextIsSelectable() function or through the XML markup. As such, the selectability of the text must be somewhat impacting the way in which the contents of the TextViews are repopulated after a rotation, although I cannot seem to establish exactly why. Any assistance would be much appreciated.
I think the problem is due to your device's configuration change. When you rotate your device a configuration change event occur. Add the following code to your Manifest.xml and your activity class. Hope, that will solve your problem.
Changes to Manifest.xml file ---> (Suppose XYZ is your activity class)
<activity android:name=".activities.XYZ"
android:configChanges="orientation|screenSize"/>
Add this to your XYZ activity class --->
#Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
}
1st
super.onSavedInstanceState(...) should be the first thing called. Your 'save' stuff should come after.
2nd
You will most likely need to save all of the textviews, so you can repopulate them, or save the array and repopulate based on that.
I found this site to be very good for explaining the ins and out of save-instance-state handling.
http://www.intertech.com/Blog/saving-and-retrieving-android-instance-state-part-1/

disable button click for certain rows of buttons

I dynamically create Buttons by entering a word. If I write "met", it appears on the screen - one Button per letter. The same thing happens for the next word I enter, and it appears below the previous word --- as shown in the image above.
When I click on a Button it turns green. My question is, what is the best way to disable the clicking of a row of Buttons. Meaning, if the user clicks on the 'm' in "met" I want the user to only be able to click on the Buttons in "met" and to not be able to click on any of the Buttons in "had", "goes", or "ran"
Here is my code:
EDIT
int size = enter_txt.getText().toString().length();
for (int i=0; i<size; i++){
final Button dynamicButtons = new Button(view.getContext());
dynamicButtons.setLayoutParams(rlp);
dynamicButtons.getLayoutParams().width = 130;
dynamicButtons.getLayoutParams().height = 130;
dynamicButtons.setTag("0");
dynamicButtons.setId(1);
dynamicButtons.setText(edit_text_array[i]);
dynamicButtons.setBackgroundResource(R.drawable.button);
button_list.add(dynamicButtons);
linearLayout2.addView(dynamicButtons, rlp);
dynamicButtons.setOnClickListener(
new View.OnClickListener()
{
public void onClick(View view)
{
int i=0;
LinearLayout ll = (LinearLayout) dynamicButtons.getParent();
for(i=0; i<list_of_ll.size();i++){
if (ll == list_of_ll.get(i)){
list_of_ll.get(i).setId(i);
break;
}
}
if(list_of_ll.get(i).getId()==i)
ButtonOnClick(view);
}
});
}
linearLayout2.setId(0);
linearLayout2.setTag("0");
list_of_ll.add(linearLayout2);
EDIT
I created a List of the LinearLayouts for each row of Buttons. The Buttons turn green if the id of the LinearLayout is set to 1. When I click on a Button I want that LinearLayout to stay at 1 and have all other rows/LinearLayouts set to 0 so they become unclickable.
Currently, every Button I click turns green even if it's in a different row. Can someone please help me solve this issue?
Why you don't set Id in the for loop so that you are able to refer and set the onlicklistener to null like jpcrow already mentioned.
Set Id like:
YourCreatedBtn.setId(i+1);
//Id's setted programmatically don't.
have to be unique... But they should be
a positive number (referring to the
android documentation)
And in your on click method simply set onclicklistener for specified Id's to null. Just a hint, hope it helps
Update regarding Thread-openers Comment
I found two simple ways but i would prefer the one which is not commented out in the buttonIsClicked:
LinearLayout llrow;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
llrow = (LinearLayout) findViewById(R.id.test_layout);
//Adding 5 Buttons
for(int i = 0; i<5; i++) {
Button mybtn = new Button(this);
//set LayoutParams here
mybtn.setId(5);
mybtn.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
// TODO Auto-generated method stub
buttonIsClicked(v);
}
});
llrow.addView(mybtn);
}
}
private void buttonIsClicked(View v) {
/*ArrayList<View> myButtons = llrow.getTouchables();
for(int i = 0; i < llrow.getChildCount(); i++){
myButtons.get(i).setOnClickListener(null);
}*/
for(int i = 0; i<llrow.getChildCount(); i++){
llrow.getChildAt(i).setOnClickListener(null);
}
}
It's just a simplified Version of your code, but i'm sure you will get the Content..
What if found out is, that you don't have to set the ID in both cases.. You can easily get all the child over
YourRowLinearLayout.getChildAt(starting from 0 to n-1-Views you added)...
I didn't found a way around the for-loop... But this small-little loop will not break your neck regarding to Performance..
The outcommented-code is the second Approach, finding all the Child over getTouchables which logically leads to an ArrayList and that's exactly the reason why i don't like it. You have to initialize an arraylist...... However, this also won't break your neck regarding to Performance but a penny saved is a penny got! ;) Hope it helps and everything is clear. Both of them work! Please mark as accepted answere if it fits your Needs...
You have to distinguish between the two rows, either add them to different ViewGroups or you can use View.setTag(int key, Object tag)

Creating custom components using regular android components

basically I want to encapsulate a simple component from code that I already have.
Basically it's a LinearLayout with buttons inside. These buttons will make changes to a ListView, and there is also some other small stuff that it will do.
Currently I have a XML layout with those, and I programmatically setup everything else: the buttons, the interaction between the list and the other small stuff.
Obviously I thought to myself, let's encapsulate this.
I started out trying to extend the LinearLayout and adding the buttons.
Already I have no idea how to inflate the buttons to add to the view
What method do I override to create this buttons just before the view gets created without messing with the measures and inflations, etc.
I've looked around but the custom components I see are either completely new components or components that simply add small functionality to the custom ones.
Is there some guidelines for doing this?
Good tutorials/examples?
Any help is appreciated. Thanks !
EDIT:
Okay, here is a little more specific stuff.
Basically I want to create a View that holds filter buttons for a ListView. This will be used in different places with different filters, so I need flexibility for the buttons.
Basically I'd like to do something like this:
CustomView view = new CustomView(activity);
view.addButton("Lala", new OnFilterClickListener {
onClick(ListView list, View v) {
// Do the filtering
}
});
mListView.addHeaderView(view);
I want the view to adapt it's weights for showing the buttons, show the user which filter is active, stuff like that.
But I still don't really know how to make those dynamically added buttons appear, where do I generate them, how to inflate them and stuff like that.
public class myLayout extends LinearLayout {
//...
public void addButton(String text, OnClickListener listener) {
Button newButton = new Button(mContext);
newButton.setText(text);
newButton.setOnClickListener(listener);
//Say we want the weights to be equal
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(0,LinearLayout.LayoutParams.Fill_PARENT, 1);
addView(newButton, params);
}
//...
}
You can even do something to the view before dispatching the click like this:
public class myLayout extends LinearLayout {
//...
public void addButton(String text, final OnClickListener listener) {
Button newButton = new Button(mContext);
newButton.setText(text);
newButton.setOnClickListener(new OnClickListener(){
public void onClick(View v) {
//do whatever you want
//like change background of button or something
//finally
listener.onClick(v);
}
});
//Say we want the weights to be equal
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(0,LinearLayout.LayoutParams.Fill_PARENT, 1);
addView(newButton, params);
}
//...
}

Categories

Resources