Auto scroll down on textviews - android

I have 3 components on my application, 1 textview(inputType:textMultiline, scrollbar:vertical, gravity:bottom|right) at the top, 1 editview at the middle and 1 button at the bottom. When I type something on my editview and I click the button, the text written on the edit view displays on the textview. Every time i hit ok, the text displays the the bottom and what is displayed is the first three inputs. I have to scroll down the textview in able for me to see my last input.
Now, I want my users to see their last input on their textview. I want to know if there is such code for auto scrolldown for textviews everytime I input a new text on it. I am new on developing android apps. Thanks!

When you set text to textview just set foucus to it. like
tv.setFocusable(true);
It will automatically focus your view whenever you change your string on textview.
If you are adding text to your text view again and again then you can try this
int scroll_amount = tv.getBottom();
tv.scrollTo(0, scroll_amount);
Hope it will work not sure..
Try this also
int scroll_amount = (int) (tv.getLineCount() * tv.getLineHeight()) - (tv.getBottom() - tv.getTop());
tv.scrollTo(0, scroll_amount);

you can use scrollView.scrollTo(x,y) to auto scroll to position that you want.
/*edit*/
create custom class for scrollview
package com.android.mypackage
public class myScrollView extends ScrollView{
private int maxY = 0;
#Override
protected void onScrollChanged(int x, int y, int oldx, int oldy) {
super.onScrollChanged(x, y, oldx, oldy);
if(y>maxY)
maxY=y;
}
public void moveToEnd(){
this.scrollTo(0, maxY);
}
}
using this custom class in layout xml as below:
...
<com.android.mypackage.myScrollView
android:id="#+id/my_scrollview"
android:layout_width="fill_parent"
android:layout_height="wrap_content">
<TextView .../>
</com.android.mypackage.myScrollView>
.....
when you press Ok button just call function myscrollViewObj.movetoEnd();
it's just a draft code, still not tested.

I already got the answer for this! Thank you for giving me some idea. I might be able to use them in the future. #Bharat Sharma, you almost got the answer! Thanks!
public void onClick(View v) {
// TODO Auto-generated method stub
log.setText(log.getText() + "\n" +input.getText());
if(log.getLineCount() > 5){
scroll_amount = scroll_amount + log.getLineHeight();
log.scrollTo(0, scroll_amount);
}
}
I called the variable scroll amount outside the onCreate(). Thanks again!

This is it.
public void onScrollDownClicked(View view) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
mTextView.scrollTo(0, mTextView.getLayout().getHeight() - mTextView.getHeight());
}
}

Related

Auto-scrolling textview in 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")

EditText - have default text that cannot be deleted, and cover text with background after leaving EditText

I am creating an email form and would like to have text in an EditText that cannot be deleted. On the screenshot below, the To could not be deleted.
If anyone has suggestions on how to achieve the above, it would be great - Thanks.
My current code for the To EditText box:
<EditText
android:id="#+id/editText2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="5dp"
android:layout_marginLeft="5dp"
android:layout_marginRight="5dp"
android:layout_marginTop="0dp"
android:hint="#string/email_to" >
</EditText>
The problem is android:hint text dissappears when the user starts to text, and android:text can be deleted by the user.
How do we have text that cannot be deleted? Thanks.
Note:
Also, I would like to note that I have a method that clears text using a clear button - this works fine - but I am hoping that it would not delete the fixed text (If I got that implemented!).. Here`s the code for that:
private void clearForm(ViewGroup group)
{
for (int i = 0, count = group.getChildCount(); i < count; ++i) {
View view = group.getChildAt(i);
if (view instanceof EditText) {
((EditText)view).setText("");
}
if(view instanceof ViewGroup && (((ViewGroup)view).getChildCount() > 0))
clearForm((ViewGroup)view);
}
}
SOLUTION:
Managed a roundabout way of doing this.. I created a TextView and EditText within a nested Linear Layout. I turned off the border in the EditText using android:background="#00000000".
I created an xml file in the drawable folder, and refered to this in the relevant linear layout like this: android:background="#drawable/customxml"
You can do it by using Text Watcher listener.
You can keep text in edittext by checking length of edittext.
For example
editText.setText("To") // it mean have lenght 2
And Than in method afterTextChanged, check if text in editText is has been deleted (with check length of text in editText)
This is for complete example code:
editText.setText("To");
editText.addTextChangedListener(new TextWatcher() {
#Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
#Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
}
#Override
public void afterTextChanged(Editable s) {
if(s.length < 2){
editText.setText("To")//set editext with "To" again like has been initialized
editText.setSelection(editText.getText().length)// to make cursor in end of text
}
}
});
Hope this help!
To get the visual appearance you want, include a horizontal LinearLayout containing a text view and an EditView. Turn off the border around the EditView (there's an attribute that does that (I think it's android:shadowColor) ) Play around with margins and padding to get them to be adjacent to each other. Set the background color on the linear layout to put a border around the combined pair.
I wouldn't worry much about efficiency. You aren't nesting very deeply. The biggest challenge is going to be getting it to look like a single view.
Edit: Another thought. If that doesn't work, you could make the "To" a drawable, and set it using the android:drawableLeft attribute.
Add TextView to the right side of EditText in LinearLayout with horizontal orientation or RelativeLayout with android:layout_toRightOf="#id/YourTextView"

In Android SDK - How can I locate the word at a pixel location pressed by the user in an EditText

I am building an application where the user enters text into an EditText.
I would like to be able to have the user touch a word, and detect the word at the pixel location they touched. Is there any way to do this?
Thanks, Victor
Try this code...
public boolean onTouch(View view, MotionEvent mEvent) {
Layout layout = ((TextView) view).getLayout();
int x = (int)mEvent.getX();
int y = (int)nEvent.getY();
if (layout!=null){
int line = layout.getLineForVertical(y);
int charAtTouch = layout.getOffsetForHorizontal(line, x);
Log.i("position", "" + charAtTouch);
}
return true;
}
You may want to take a look at Spans. Particularly, ClickableSpan. You can assign a span to a part of the text to give that part of the text some "features" (in this case, it will make the text clickable).
There's a nice example, in which custom Spans extending ClickableSpan are used in EditText to make the text display more user-friendly.
May be you can get position of cursor appeared in textbox after touch and locate the word?

Permanent hint in EditText

I have a situation where I would like the user to complete a sentence for me. For example, consider a EditText with a hint of "The last time I ". Normally, when a user clicks an EditText, the hint disappears, but I would like it to stay. Additionally, I would like the text to be permanent, so that it cannot be erased... leaving the user with only one option... complete the sentence.
The first part is fairly simple, just use the setText() method of EditText to place the hint. The difficult part is the latter. How can I have text in an EditText that the user cannot erase?
Well couldn't you do it in code? Some algorithim like, if the text is less than 16 characters (length of "The last time I ") then set the text to that. Therefore whenever they clicked it, if they tried to erase it, it would just go back to the default text.
Also, another idea..why don't you just make a TextView thats right edge aligns with the left edge of the EditText box, the user would never know that it was another box. This is acutally the best solution, if you don't want the text ever to be edited, just make it a TextView
Described problem can be solved using android.text.TextWatcher.
public class CompleteSentenceWathcher implements TextWatcher {
private final String initialText;
private int start;
private int after;
private int count;
public CompleteSentenceWathcher(String initialText) {
this.initialText = initialText;
}
#Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
this.start = start;
this.count = count;
this.after = after;
}
#Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
}
#Override
public void afterTextChanged(Editable s) {
if(start < initialText.length()) {
if(s.toString().startsWith(initialText)) {
return;
}
if(count >= 1 && after == 0) {
if(start+count+1 <= initialText.length()) {
s.replace(start, start+count, initialText.substring(start, start+count+1));
} else {
s.replace(start, start, initialText.substring(start, start+1));
}
} else if(count == 0 && after >= 1) {
s.delete(start, start+after);
}
}
}
}
Create an instance of EditText and add the TextWatcher.
EditText editText = new EditText(this);
editText.setText("I love");
editText.addTextChangedListener(new CompleteSentenceWathcher(editText.getText().toString()));
I've implemented this with an InputFilter, where _PERMANENT_HINT_TEXT is the text at the end of the EditText that I don't want the user to be able to modify. I recommend adding a color span to it, so that it is grayed out to hopefully look like a hint/disabled section of text. This should hopefully improve the UX as they should automatically assume it is unmodifiable, and not just wonder why some part of the EditText (that they usually can completely change) isn't "working". This approach allowed the text to be set after
the InputFilter was set on the EditText, which was a requirement for me since I used this on an EditTextPreference.
To be clear, I needed the permanent text to exist at the end of the EditText, instead of the beginning, but that should be symmetrical to my implementation.
new InputFilter() {
#Override
public CharSequence filter(CharSequence source, int source_start, int source_end,
Spanned destination, int destination_start, int destination_end) {
final int protected_text_start = (TextUtils.isEmpty(destination)? source.length() : destination.length()) - _PERMANENT_HINT_TEXT.length();
// Allows input into unprotected region
if (source_start + destination_start - source_end < protected_text_start)
return null;
// Prevents deletion of protected region
else if (TextUtils.isEmpty(source))
return destination.subSequence(destination_start, destination_end);
// Ignores insertion into protected region
else
return "";
}
}
use EditText.setFilters(new InputFilters[] { /* InputFilter goes here */ }; to add it to the desired EditText.
Just checking for the length wouldn't be adequate... I could type "This is a really long text I put into the box" and it would accept it even though it doesn't begin with "The last time I" string.
Personally, I would probably go for the prevention method suggested of using a TextView over that of a check on the way out. But if you're going to validate it afterwards, you'd actually need to check the beginning of the returned string.

Android: why is my OnKeyListener() not called?

I defined an EditText-field and I want to be informed when the user edits that fields.
So I thought: simple - I add an OnKeyListener and so I did. But even though the text field gets edited (and even displays the entered/modified text) I don't get any callback, i.e. the LOG-output doesn't show up.
TextView text = new TextView(this);
text.setText(...);
...
text.setOnKeyListener(new OnKeyListener()
{
public boolean onKey(View v, int keyCode, KeyEvent event) {
TextView tv = (TextView)v;
CharSequence val = tv.getText();
Log.v(TAG, "value: " + val);
// ... rest omitted for brevity
}
});
Any idea, why that callback is never called?
Michael
PS.: Sigh! Android is really full of oddities! It seems that almost nothing I touched so far worked immediatly as one would expect. And - believe it or not - I have LOTS of experience with GUIs, esp. in Java (AWT, Swing, SWT, you name it...) But Android is a really tough beast!
Are you using the soft keyboard (ime) to type in the edit text? I believe that the onKeyListener only gets invoked with events from the hardware keyboard. You are better off using the TextWatcher if you can. onKeyListener not working with soft keyboard (Android)
I had the exact same problem, but on only 1 of my Android apps and I never did figure out what the difference was.
My solution though was to do what Mayra suggested and add a TextWatcher to handle the TextChanged events. So it works no matter how the text entry occurs.
editName.addTextChangedListener(new TextWatcher () {
public void afterTextChanged(Editable s) {
Button btnSave = (Button)findViewById(R.id.btnSaveToon);
if(s.length() > 0)
btnSave.setEnabled(true);
else
btnSave.setEnabled(false);
}
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
// TODO Auto-generated method stub
}
public void onTextChanged(CharSequence s, int start, int before, int count) {
// TODO Auto-generated method stub
}
});
Works like a charm in the emulator and on my HTC Inspire
You say that you're dealing with an EditText, but your code refers to a TextView. My guess is that you have an EditText in your layout XML files, but you're referring to this newly created TextView in your code, which is in fact not even in the app's UI at all.
If there is already an EditText in your layout XML file, then you need to get a pointer to it in your Java code, probably using the findViewById() method. Then add your OnKeyListener to that EditText.
Defining your layout in XML actually makes a lot more sense (at least in many, if not most, cases) than defining it one component at a time and then adding each those components to the UI, like you do in Swing. But it takes some getting used to, no question.
I had the same problem. The goal of the EditText was to input an amount of a currency, so I only needed the KeyEvents because I wanted the amount to be written from back to front as in apps like PayPal. I ended up just generating my own soft keyboard at the bottom of a RelativeLayout using a Fragment that is toggled with its visibility. If anyone wants to use my code, here you go:
The Keyboard Fragment Class:
public class KeyboardFragment extends Fragment {
private LinearLayout keyboard1, keyboard2, keyboard3, keyboard4, keyboard5, keyboard6,
keyboard7, keyboard8, keyboard9, keyboard0, keyboardReturn, keyboardApply;
private KeyboardListener keyboardListener;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_keyboard, container, false);
keyboard0 = view.findViewById(R.id.keyboard0);
keyboard1 = view.findViewById(R.id.keyboard1);
//and so on...
keyboard0.setOnClickListener((View v) -> {
keyboardListener.keyPressed(0);
});
keyboard1.setOnClickListener((View v) -> {
keyboardListener.keyPressed(1);
});
//and so on...
return view;
}
#Override
public void onAttach(#NonNull Context context) {
super.onAttach(context);
try {
keyboardListener = (KeyboardListener) context;
} catch (ClassCastException e) {
throw new ClassCastException(context.toString() + "must implement KeyboardListener");
}
}
public interface KeyboardListener {
public void keyPressed(int key);
}
}
The overlaying activity needs to implement the KeyboardListener and override the keyPressed function.
The XML of the Fragment looks like this:
Drawables:
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="#color/ui_keyboard_dark" />
<corners android:radius="5dp" />
</shape>
Then I inflated a container in the overlaying activity with the fragment and set its height to a moderate fraction of the screen's height:
KeyboardFragment fragment = new KeyboardFragment();
FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginTransaction();
fragmentTransaction.add(R.id.keyboardContainer, fragment);
fragmentTransaction.commit();
DisplayMetrics displayMetrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);
RelativeLayout.LayoutParams layoutParams = new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,(int) ((double)displayMetrics.heightPixels / 2.8));
layoutParams.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM);
keyboardContainer.setLayoutParams(layoutParams);
Set a boolean that tracks the state of the keyboard in the activity. Then set the visibility according to actions like onBackPressed() or the click of the apply or ok button of the keyboard.
The amount of the currency is tracked using an int that would represent 10,95€ like this: amount = 1095
Then you just need to multiply the amount by 10 and add the pressed number. When pressing backspace just divide by 10.
I hope someone faces the same problem as me and finds this useful :).

Categories

Resources