I have a scrollview containing a textview in an Android app. This textview will have text appended to it continuously at set intervals. Scrolling works and the text adds just fine, but what I'd like to do is have the scrollview autoscroll down as text is added. As new text is appended at the bottom, it automatically scrolls down to match and old text is pushed out of sight at the top. What would be even better is to add text to the bottom of the scrollview and have it push older text upward, but one thing at a time.
<ScrollView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:fillViewport="true"
>
<TextView
android:id="#+id/statusText"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_gravity="bottom" />
</ScrollView>
I've tried the solution with gravity but result was a lot of empty space under actual text when scroll to bottom.
So that is another way. You can place your TextView inside of ScrollView:
<ScrollView
android:id="#+id/scrollView1"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:fillViewport="true" >
<TextView
android:id="#+id/textView1"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</ScrollView>
And define addTextChangedListener
public class MainActivity extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
//some code here
ScrollView scrollView1 = (ScrollView) findViewById(R.id.scrollView1);
TextView textView1 = (TextView) findViewById(R.id.textView1);
textView1.addTextChangedListener(new TextWatcher() {
#Override
public void afterTextChanged(Editable arg0) {
scrollView1.fullScroll(ScrollView.FOCUS_DOWN);
// you can add a toast or whatever you want here
}
#Override
public void beforeTextChanged(CharSequence arg0, int arg1,
int arg2, int arg3) {
//override stub
}
#Override
public void onTextChanged(CharSequence arg0, int arg1, int arg2,
int arg3) {
//override stub
}
}) }
Actually i noticed right now that the application will scroll every time the text is changed, not just added. However it works just fine for me.
You can simply scroll the whole ScrollView to the bottom when text is added.
textView.append(text);
scrollView.fullScroll(View.FOCUS_DOWN);
As #RaphaelRoyer-Rivard suggested in his comment, you can get more solid result with post:
textView.append(text);
scrollView.post(() -> scrollView.fullScroll(View.FOCUS_DOWN));
If you're looking for a more generic way of auto-scrolling to the bottom of a ScrollView, you could try the snippet below. It has the advantage that you don't need to post a Runnable to make sure you're in the UI thread. It has the disadvantage that I'm not sure what things this could break.
public class AutoScroller extends ScrollView {
public boolean autoscroll = true;
public AutoScroller(Context context) {super(context);}
public AutoScroller(Context context, AttributeSet attrs) {super(context,attrs);}
public AutoScroller(Context context, AttributeSet attrs, int defStyleAttr){super(context, attrs, defStyleAttr);}
public AutoScroller(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {super(context, attrs, defStyleAttr, defStyleRes);}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
if(autoscroll) {
scrollTo(getScrollX(), getChildAt(getChildCount()-1).getBottom() - getHeight());
}
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
}
Another candidat to override might be onLayout.
textView.append(text);
scroll.fullScroll(View.FOCUS_DOWN)
this will give problem, it won't scroll upto extreme top, due to android:layout_gravity="bottom"
Related
I am using a TextView which has to rotate at angle at 35 degrees. I have successfully created a rotated textview but I can't fill the complete screen. Basically I want to complete 2 objectives-
1)Make the textview occupy the whole screen
currently it is showing it like this-
I wanna fill the space left in the top left corner. I covered the other three corners by the help of #Alexandre's answer. #Thanks Alexandre.
CustomTextView.java
public class CustomTextView extends android.support.v7.widget.AppCompatTextView {
public CustomTextView(Context context) {
super(context);
}
public CustomTextView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public CustomTextView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
setMeasuredDimension(widthMeasureSpec*2, heightMeasureSpec*3);
}
}
Layout
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/linearLayout"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_alignParentTop="true"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:orientation="vertical">
<com.dakshansh.partytimevendorsapp.CustomTextView
android:id="#+id/text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:rotation="-35"
<!--Hope I don't need to change text.Text is too much more.-->
android:text="Galleries" />
</LinearLayout>
2)Make the text justified
As you can see my text is left aligned & as I am trying to create a pattern, I would require the text to be justified
One solution would be to create a CustomTextView that extends TextView overriding [onMeasure](https://developer.android.com/reference/android/view/View.html#onMeasure(int, int)).
For example :
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
setMeasuredDimension(widthMeasureSpec*2, heightMeasureSpec*2);
}
This would make your textView 4 times bigger filling the white spaces.
PS: I haven't tried this code.
How do I keep gridView from needing to scroll by auto adjusting it's height? I would like all items, no matter how many items I add to the gridView to remain on screen without scrolling. Is this possible?
Here is my UI so far.
<?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="match_parent"
android:background="#drawable/bg"
android:orientation="vertical">
<GridView
android:id="#+id/gv"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_below="#+id/tv_header"
android:fadingEdge="none"
android:focusable="false"
android:focusableInTouchMode="false"
android:gravity="center"
android:listSelector="#00000000"
android:numColumns="auto_fit"
android:stretchMode="columnWidth" />
</LinearLayout>
I did try adding a weightSum to the root and weight to gridView but it still requires scrolling.
Update: I also tried using a custom gridview. This did not work, but here is my attempt anyway.
public class CustomGridView extends GridView {
public CustomGridView(Context context) {
super(context);
}
public CustomGridView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public CustomGridView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
#Override
public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(MEASURED_SIZE_MASK, MeasureSpec.AT_MOST));
getLayoutParams().height = getMeasuredHeight();
}
}
Thanks in advance!
I have found answer to this. You can set the height of each item in the adapter by using
view.setLayoutParams(new GridView.LayoutParams(GridView.AUTO_FIT, resizeValue));
resizeValue is the size that you want to adjust your rows to. To get resizeValue you can pass to the adapter mResizeValue based on the calculations relative to your device screen size. Something like
resizevalue = this.getResources().getDisplayMetrics().heightPixels / (NUM_COLS);
I figured out some other ways of calculating the height of each row based on screen size and then doing something similar, however, this requires that you do these calculations after you set your adapter and then update the changes to the adapter. It seems less efficient but I will share that methodology as well.
private void resizeGridView(GridView gridView, int items, int columns) {
ViewGroup.LayoutParams params = gridView.getLayoutParams();
int oneRowHeight = gridView.getHeight();
int rows = (int) (items / columns);
params.height = oneRowHeight * rows;
gridView.setLayoutParams(params);
}
Then after you set your adapter use
gridView.getViewTreeObserver().addOnGlobalLayoutListener(new
ViewTreeObserver.OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
if (!gridViewResized) {
gridViewResized = true;
resizeGridView(gridView, numItems, numColumns);
}
}
});
I have a project where I need to programmatically create a TextView and place it inside a custom ViewGroup. When I instantiate the textView, I set its layoutParams, text, gravity etc, then add it to the ViewGroup. Then in the onLayout() method of the ViewGroup i try to position it (as recommended by google guidelines).
Everything is fine EXCEPT for the textView's text gravity. Only center_horizontal seems to work. The entire problem can be boiled down to a few lines of code as below:
public class Custom extends ViewGroup {
TextView mTv;
public Custom(Context context, AttributeSet attrs) {
super(context, attrs);
mTv = new TextView(context);
mTv.setText("can't touch this");
mTv.setGravity(Gravity.CENTER);
ViewGroup.LayoutParams params=new ViewGroup.LayoutParams(LayoutParams.MATCH_PARENT,LayoutParams.MATCH_PARENT);
mTv.setBackgroundColor(context.getResources().getColor(R.color.paleblue));
this.addView(mTv,params);
}
public Custom(Context context) {
this(context,null);
}
#Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
mTv.layout(l,t,r,b);
}}
And the XML code:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:custom="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<com.apps.renegade.testbook.Custom
android:layout_width="match_parent"
android:layout_height="match_parent"
/>
</RelativeLayout>
The text should be in the center of the screen. Can anyone explain why is this happening? Thanks
If you want to override a ViewGroup, check this, this is the official document which shows you how to extend a ViewGroup.
I have made a Custom ViewGroup. Detail about this Custom ViewGroup is found here from my earlier question. The problem I am facing here is , whenever I try to add buttons in the LinearLayout that is inside the custom Viewgroup, the buttons never get shown. I have tried many things but the button is never displayed, do I have to do something in the custom viewgroup, I even tried inflating the button but still did not work.
Code for Custom ViewGroup:
public class RootViewLayout extends ViewGroup {
private View mDrawView;
private View mSlideView;
private int mTop;
private int mDragRange;
public RootViewLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}
#Override
protected void onFinishInflate() {
mDrawView = findViewById(R.id.content_frame_white);
mSlideView = findViewById(R.id.slide_frame);
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
setMeasuredDimension(widthSize, heightSize);
}
#Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom){
bringChildToFront(mDrawView);
mDrawView.layout(0, 0, right, bottom);
mSlideView.layout(0, 0, right/2, bottom);
}
}
and the XML :
<com.example.drawapp.RootViewLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/Root_View_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#color/white">
<com.example.drawapp.DrawView
android:id="#+id/content_frame_white"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#drawable/whitepaperwithcoffeestain">
</com.example.drawapp.DrawView>
<LinearLayout
android:id="#+id/slide_frame"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:background="#drawable/slidebackgrd"
android:orientation="vertical">
<Button
android:id="#+id/pen"
android:layout_width="100dip"
android:layout_height="wrap_content"
android:background="#drawable/pic"/>
</LinearLayout>
</com.example.drawapp.RootViewLayout>
sorry for the late comment.if you didn't solve it yet,apparently you have to do the same to your button what you did for your LinearLayout
View b= mSlideView.findViewById(R.id.pen);
b.layout(0, 0, right/4, bottom);
checked it and it displayed the buttons width as half of the linearlayout and height as the bottom parameter
Looking to make a small Android Compound View/Custom Component, where a FrameLayout is extended so that it has its own fully overlaying "Cover" view (that can intercept clicks and ripple) without repeating that cover in every XML instance.
I don't know whether it is best practice to re-layout the covering View in the Custom FrameLayout's onMeasure or onLayout methods, or if there's a simple XML trick that I'm missing (outside of just using a RelativeView since that would draw and re-draw the layout very often, but if I'm wrong here too correct me please)
CoveredFrameLayout.class
public class CoveredFrameLayout extends FrameLayout {
private View coverView;
public CoveredFrameLayout(#NonNull Context context) {
this(context, null);
}
public CoveredFrameLayout(#NonNull Context context, #Nullable AttributeSet attrs) {
this(context, attrs, 0);
}
public CoveredFrameLayout(#NonNull Context context, #Nullable AttributeSet attrs, #AttrRes int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context);
}
private void init(Context context) {
inflate(context, R.layout.widget_covered_framelayout, this);
//
setBackgroundColor(ContextCompat.getColor(context, R.color.light_red));
coverView = findViewById(R.id.widget_cover);
//
coverView.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View view) {
coverView.animate()
.alpha(0)
.setDuration(1000)
.withEndAction(new Runnable() {
#Override
public void run() {
coverView.setVisibility(GONE);
}
})
.start();
view.setOnClickListener(null);
}
});
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
#Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
}
}
widget_covered_framelayout.xml
<?xml version="1.0" encoding="utf-8"?>
<merge xmlns:android="http://schemas.android.com/apk/res/android">
<View
android:id="#+id/widget_cover"
android:layout_width="20dp"
android:layout_height="20dp"
android:background="#color/transparent_yellow400"
android:clickable="true"
android:foreground="?attr/selectableItemBackground"
android:visibility="visible" />
</merge>
The XML CoveredFrameLayout being used
<com.example.oliver.content.ui.widgets.CoveredFrameLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="8dp"
android:text="hello" />
</com.example.oliver.content.ui.widgets.CoveredFrameLayout>
And here's a screenshot of what this is looking like currently. Just trying to learn the best practice to get that yellow square to dynamically cover the whole FrameLayout! Thanks y'all!
Here is the end goal result, however this result was only achieved by hardcoding the Cover View's height and width to the pre-calculated height and width in pixels of the view itself. A dynamic version of this could work fine, but I don't know what is the best practice for where in the onMeasure/onLayout cycle to put this code.