I have a TextView within a ScrollView, which currently scrolls to the bottom of the TextView.
The TextView is filled dynamically constantly updating (the TextView is essentially acting as an actions console).
However, the problem I am having is that when the dynamic text is added to the ScrollView, the user can scroll past the text into black space, which is increasing everytime more content is added to the TextView.
I have tried various different apporaches however none of these gave the right outcome. I cannot use maxLines or define height of the layouts as I need this to be dynamic for the various screen sizes, which the number of lines visible constantly changing.
I had also orginally done this progromatically, however this was crashing at random time and therefore would like to keep it in my layout (better usabilty), example code below:
final int scrollAmount = update.getLayout().getLineTop(update.getLineCount()) - update.getHeight();
if(scrollAmount > 0)
{
update.scrollTo(0, scrollAmount);
}
The code below is my current layout xml being used to automatically scroll my TextView to the bottom as content is added:
<ScrollView
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_above="#+id/spacer2"
android:layout_below="#+id/spacer1"
android:fillViewport="true" >
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="vertical" >
<TextView
android:id="#+id/battle_details"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:textSize="12dp"
android:layout_gravity="bottom" />
</LinearLayout>
</ScrollView>
EDIT - This is the code I am using to add text to my TextView:
private void CreateConsoleString()
{
TextView update = (TextView)findViewById(R.id.battle_details);
String ConsoleString = "";
// BattleConsole is an ArrayList<String>
for(int i = 0; i < BattleConsole.size(); i++)
{
ConsoleString += BattleConsole.get(i) + "\n";
}
update.setText(ConsoleString);
}
EDIT 2 - I add content to the BattleConsole like this:
BattleConsole.add("Some console text was added");
CreateConsoleString();
To sum up my only issue is the ScrollView and/or TextView is adding blank space to the bottom rather than stop the user from scrolling at the last line of text. Any help or guidence as to where I am going wrong would be much appreciated.
It looks like that when you call
BattleConsole.get(i)
it sometimes returns an empty String so you are just basically adding new lines to your TextView.
You can do this for example:
StringBuilder consoleString = new StringBuilder();
// I'm using a StringBuilder here to avoid creating a lot of `String` objects
for(String element : BattleConsole) {
// I'm assuming element is not null
if(!"".equals(element)) {
consoleString.append(element);
consoleString.append(System.getProperty("line.separator")); // I'm using a constant here.
}
}
update.setText(consoleString.toString());
If you could post the code of BattleConsole I could help you more.
As a footnote: it is encouraged to use camelCase in java. Only class names start with capital letters in java according to the convention.
Related
I'm trying to programmatically add views to a FlexboxLayout. When the layout has reached 3 wrapped lined, I want to stop adding things to the layout. The maxLine attribute wasn't useful as it starts to add too much on each horizontal line.
<com.google.android.flexbox.FlexboxLayout
android:id="#+id/dynamic_button_layout"
android:layout_width="wrap_content"
android:layout_height="584dp"
android:layout_gravity="center"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
android:animateLayoutChanges="true"
android:orientation="horizontal"
app:alignContent="center"
app:alignItems="center"
app:flexDirection="row"
app:flexWrap="wrap"
app:justifyContent="center"/>
I initially tried the following in onCreate
int viewNumber = 0;
for (Button button : buttons){
flexboxLayout.addView(button);
if (flexboxLayout.getFlexLines().size() > 2){
flexboxLayout.removeViewAt(viewNumber);
break;
}
viewNumber++;
}
The idea being if I've gone onto the 4th flex line, I just remove the last view and stop adding more views. The issue is that .getFlexLines() is always empty
If I do something like this, however, I the flex lines are returned correctly and I can start removing views. The problem with this is you can very briefly see the 4th line of buttons before they're removed.
getWindow().getDecorView().post(new Runnable() {
#Override
public void run() {
final FlexboxLayout flexboxLayout = findViewById(R.id.dynamic_button_layout);
int lines = flexboxLayout.getFlexLines().size(); // this will be 4
}
});
Is there any way to get the number of flex lines before the UI is actually updated?
Inside CursorAdapter's bindView() I bind data to the following layout:
A TextView and two Buttons : "UP" and "DOWN".
The TextView is defined in XML like so:
<TextView
android:id="#+id/tv_content"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingLeft="25dp"
android:paddingRight="25dp"
android:paddingTop="25dp"
android:scrollbars="vertical"
android:textAlignment="textStart"
android:textColor="#5c6284"
app:autoSizeMaxTextSize="40sp"
app:autoSizeMinTextSize="20sp"
app:autoSizeTextType="uniform" />
A vertical scrolling behavior is applied to the TextView, which is being controlled by the "UP and "DOWN" Buttons.
I would like to determine if the TextView requires scrolling ( is long enough to not fit its provided drawing area ) so that I can enable/disable the "UP" and "DOWN" buttons accordingly.
I'm currently reading BaseMovementMethod's scrollDown function, thinking of applying its measuring logic to my adapter, though I have the feeling that it should be much simpler. Maybe a built in behavior that I'm not aware of.
Is there a better way to achieve this, other than my suggested approach?
What I would do is put the textview inside a scrollview like so:
<ScrollView
android:id="#+id/scroller"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fillViewport="true">
<TextView
android:id="#+id/tv_content"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="test texts here"/>
</ScrollView>
In your activity, execute these lines:
boolean needScrolling = false;
if(scroller.getHeight() < tv_content.getHeight()) needScrolling = true;
You can use Static Layout class. If you set it up with your TextView's parameters you'll be able to calculate the height of the rendered text.
Layout.Alignment alignment = Layout.Alignment.ALIGN_NORMAL;
float spacingMultiplier = 1;
float spacingAddition = 0;
boolean includePadding = false;
StaticLayout myStaticLayout = new StaticLayout(text, myTextView.getPaint(), myTextView.getWidth(), alignment, spacingMultiplier, spacingAddition, includePadding);
float height = myStaticLayout.getHeight();
Then you can compare the height of your text and height of your TextView and figure out if it will require scrolling or not.
You can also try to manually create a Paint object with your min text size if myTextView.getPaint() approach does not work.
Calculate mTextView's height without data and with data and then compare it
mTextView.post(new Runnable() {
#Override
public void run() {
int lineHeight=mTextView.getCompoundPaddingBottom()+ mTextView.getCompoundPaddingTop()+mTextView.getLineHeight();
int height=mTextView.getHeight()-(mTextView.getCompoundPaddingTop()+mTextView.getLineHeight());
if (height>lineHeight){
}
}
});
I want to get EditText selection start when user click in EditText (touch).
I do with this code :
int startIndex = txtMean.getSelectionStart();
this always return 0;
and EditText xml code:
<EditText
android:id="#+id/txtMean"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#null"
android:clickable="true"
android:focusable="true"
android:focusableInTouchMode="true"
android:hint=""
android:inputType="textMultiLine"
android:scrollbars="none" />
my code work in android 2.* but don't work in 4.*
txtMean.getSelectionStart();
getSelectionStart doesn't relate to the user's last touch or click per se. It relates to the text selection on the screen. By default, when the user does a long click text-handles will come up, and allow the user to expand a highlighted text selection. It's the highlighted text that refers to the selection, which is not necessarily where the user last touched.
When the user makes a text selection it becomes highlighted because the EditText will apply a SelectionSpan to the character sequence, and getSelectionStart will return the start value of this span.
Update, Solution Help:
#Override
public boolean onTouchEvent(MotionEvent event) {
// this = EditText;
// will give you the position of the nearest chracter.
int offset = this.getOffsetForPosition(event.getX(), event.getY());
I have an app under construction. In one of the sub-menus I have a need for generic display of buttons, and therefor I want to make an activity that can display the given number of needed buttons.
I have succesfully made this happen, programmatically, but I want the total grid of buttons to fill up the entire parent they are placed in, which happens to be 3/4 of a landscape screen. The number of buttons varies from 16-38.!
I have also succesfully made this happen with other grids of buttons, in xml, with weight values, and match_parent values of entries.
When I assign buttons or rows the match_parent value programatically, it occupies the entire parent layout, not sharing it like i expect it to do, even though they have the same weight value of 1.0f
The relevant code follows below. I would like to post images as well, but I have not the reputation to do so.
`LinearLayout layout = (LinearLayout) findViewById(R.id.linear_custom_draw);
layout.setOrientation(LinearLayout.VERTICAL);
int columns = Math.min(6, 4+category); //sets number of buttons per row to 4-6
for (int i = 0; i < 4+category; i++) {
LinearLayout row = new LinearLayout(this);
row.setLayoutParams(new android.view.ViewGroup.LayoutParams(android.view.WindowManager.LayoutParams.MATCH_PARENT,
android.view.WindowManager.LayoutParams.MATCH_PARENT));
//the line above is the line that fills up the entirety of the linearlayout, even though there are more entries, unlike my xml-defined attempts.
row.setOrientation(LinearLayout.HORIZONTAL);
row.setWeightSum(1.0f);
if(i%2 == 0){
row.setBackgroundColor(getResources().getColor(R.color.listview_red_backgr_color));
}
for (int j = 0; j < columns; j++) {
int index = (i*columns)+j;
if(formations.size() > index){
Button btnTag = new Button(this);
btnTag.setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));
btnTag.setText(formations.get(index).getName());
btnTag.setTextColor(getResources().getColor(R.color.black_overlay));
btnTag.setId(formations.get(index).getId());
row.addView(btnTag);
}
}
layout.addView(row);`
Try to use TableLayout. Each Row will enforce the entire elements to match the parent with the same wights. You can control number of Buttons into each Row programatically with counter. Loop for end of Counter adding your buttons then add new Table Row
TableLayout tbl=new TableLayout(context);//create table
TableRow tr=new TableRow(context);//create table row
tr.addView(view);//add your button instead of the view
tbl.addView(tr);//add the row into the Table
In the XML file
<TableLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/keypad"
android:orientation="vertical"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:stretchColumns="*">
<TableRow>
<Button android:id="#+id/keypad_1" android:text="#string/_1"></Button>
<Button android:id="#+id/keypad_2" android:text="#string/_2"></Button>
<Button android:id="#+id/keypad_3" android:text="#string/_3"></Button>
</TableRow>
</TableLayout>
I just want to add a new line somehow to my linear layout:
layout = (LinearLayout) findViewById (R.id.layout);
... //some other code where I've appended some strings already
final TextView nline = new TextView(this);
nline.setText(Html.fromHtml("<br>")); //i also tried: nline.setText("\n");
layout.addView(nline);
But this just adds a few spaces. Can someone help me out? Thanks.
First you need to make your TextView to be multiline. And then use simple "\n" string for linebreak.
final TextView nline = new TextView(this);
nline.setSingleLine(false);
nline.setText("first line\n"+"second line\n"+"third line");
If you just want to have some empty space between two other views, you could do this in your XML (assuming you're using XML for the layout). Something like this could work, basically putting in a View with a transparent background and given height. This is assuming you have whatever parameters you want in your TextViews.
<TextView />
<View android:background="#00000000"
android:layout_height="12dp" //or whatever density pixel height you want
android:layout_width="fill_parent" />
<TextView />
Also, in what you tried above... you could try a space and newline... that might work.
nline.setText(" \n");
You may need to set the InputType to TYPE_TEXT_FLAG_MULTI_LINE using the setInputType() method of TextView
tv.setInputType(tv.getInputType()|InputType.TYPE_TEXT_FLAG_MULTI_LINE);
You also could just set some margin/padding on the other views. I think that TextViews should not be misused as spacers.
Simple as:
String hello = getResources.getString(R.string.hello);
String world = getResources.getString(R.string.world);
textView.setText(hello + "\n" + world);
Simple by giving "\n" data is displayed in a new line
txtView.setText("Latitude :" + latitude + "\nLongitude :" + longitude);