Force RecyclerView to bottom of page in layout - java

I have a page that consists of an ImageView, and a RecyclerView. The RecyclerView contains a small number of items (currently three) and only takes up around a quarter of the screen on my test device. However, despite trying numerous layout options, I cannot get the RecyclerView to effectively wrap its content and take up just enough space required to contain these three rows, and leave the rest of the space for my ImageView.
To help illustrate what I mean, I have drawn two diagrams. The first shows what I would like to happen, and the second what is happening:
So far, I have tried several different combinations of RelativeLayout - for instance, I will set RecyclerView to layout:align_ParentBottom and the second RelativeLayout that contains the ImageView to layout:alignComponent so that its bottom matches the RecyclerView top (this would normally drag the ImageView layout so that it fills any reminaing space, which is what I would like to happen.)
The RecyclerView though just keeps occupying all the space it can, even though it only contains a few rows. The current "solution" I have is to place everything inside a LinearLayout and set less gravity to the RecyclerView, but it isn't ideal, because on different emulators it wont line up completely with the bottom of the screen, and in others there isn't enough room and the RecyclerView becomes scrollable.
Thanks in advance for any help and suggestions anyone can offer.

Many thanks to everyone who contributed, but I have found a programmatic solution outside of the layout files. In case anyone else is looking for a solution to this problem, I found one here.
It appears as if there is an issue with RecyclerView currently where it doesn't wrap content. The answer is to construct a custom class that extends LinearLayoutManager. I have posted the solution that worked for me below - most of it is copy and pasted from the answer given in the link I quoted. The only small issue is that it doesn't account for the extra space added by decorations, which is why I had to make a small tweak to the following line near the end of the code:
//I added the =2 at the end.
measuredDimension[1] = view.getMeasuredHeight() + p.bottomMargin + p.topMargin + 2;
Here is the code in its entirety:
public class HomeLinearLayoutManager extends LinearLayoutManager{
HomeLinearLayoutManager(Context context, int orientation, boolean reverseLayout) {
super(context, orientation, reverseLayout);
}
private int[] mMeasuredDimension = new int[2];
#Override
public void onMeasure(RecyclerView.Recycler recycler, RecyclerView.State state,
int widthSpec, int heightSpec) {
final int widthMode = View.MeasureSpec.getMode(widthSpec);
final int heightMode = View.MeasureSpec.getMode(heightSpec);
final int widthSize = View.MeasureSpec.getSize(widthSpec);
final int heightSize = View.MeasureSpec.getSize(heightSpec);
int width = 0;
int height = 0;
for (int i = 0; i < getItemCount(); i++) {
measureScrapChild(recycler, i,
View.MeasureSpec.makeMeasureSpec(i, View.MeasureSpec.UNSPECIFIED),
View.MeasureSpec.makeMeasureSpec(i, View.MeasureSpec.UNSPECIFIED),
mMeasuredDimension);
if (getOrientation() == HORIZONTAL) {
width = width + mMeasuredDimension[0];
if (i == 0) {
height = mMeasuredDimension[1];
}
} else {
height = height + mMeasuredDimension[1];
if (i == 0) {
width = mMeasuredDimension[0];
}
}
}
switch (widthMode) {
case View.MeasureSpec.EXACTLY:
width = widthSize;
case View.MeasureSpec.AT_MOST:
case View.MeasureSpec.UNSPECIFIED:
}
switch (heightMode) {
case View.MeasureSpec.EXACTLY:
height = heightSize;
case View.MeasureSpec.AT_MOST:
case View.MeasureSpec.UNSPECIFIED:
}
setMeasuredDimension(width, height);
}
private void measureScrapChild(RecyclerView.Recycler recycler, int position, int widthSpec,
int heightSpec, int[] measuredDimension) {
View view = recycler.getViewForPosition(position);
if (view != null) {
RecyclerView.LayoutParams p = (RecyclerView.LayoutParams) view.getLayoutParams();
int childWidthSpec = ViewGroup.getChildMeasureSpec(widthSpec,
getPaddingLeft() + getPaddingRight(), p.width);
int childHeightSpec = ViewGroup.getChildMeasureSpec(heightSpec,
getPaddingTop() + getPaddingBottom(), p.height);
view.measure(childWidthSpec, childHeightSpec);
measuredDimension[0] = view.getMeasuredWidth() + p.leftMargin + p.rightMargin;
measuredDimension[1] = view.getMeasuredHeight() + p.bottomMargin + p.topMargin + 2;
recycler.recycleView(view);
}
}
}
Thanks again.

Put the two in a RelativeLayout and make ImageView fill parent:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent" android:layout_height="match_parent">
<RecyclerView
android:id="#+id/recyclerView"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"/>
<ImageView
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_above="#id/recyclerView"
/>
</RelativeLayout>
Edit: Wrote TextView by accident. Fixed.

The only solution I can think of is using layout weight. Specify 70% of the screen for the image and 30% for the Recyclerview as you said you have just 3 rows. Use adjustViewByBounds to ensure the images aspect ratio is maintained.
My code below:
<?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:orientation="vertical">
<ImageView
android:src="#drawable/ic_round_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:adjustViewBounds="true"
android:layout_weight=".9"/>
<android.support.v7.widget.RecyclerView
android:id="#+id/recyclerView"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_weight=".1"/>
</LinearLayout>

Related

Layout stretched after inserting it into a ScrollView

I have a Layout with a ScrollView where I want to programatically insert another layout. So this
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="96dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:background="#56565A"
android:orientation="vertical">
inserted into this
<HorizontalScrollView
android:id="#+id/live_scroll_view"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<LinearLayout
android:id="#+id/live_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal" />
</HorizontalScrollView>
And I insert it like that
for (int i = 0; i < exercise.targetSets; i++) {
LayoutInflater factory = LayoutInflater.from(getContext());
View myView = factory.inflate(R.layout.live_set_item, null);
TextView setName = (TextView) myView.findViewById(R.id.current_set);
setName.setText(String.format("SET %d", i+1));
scrollView.addView(myView);
}
But when I run the app the inserted layout is twice as wide as it should.
How it should look (How it looks in the Android Studio Editor)
Vs how it looks when inserted into the ScrollView
So far I have tried to use a ConstraintLayout which didn't work. Or using different layout weights.
I hope you can help me with this.
You can try use the Layout Parameters for setting view custom params for height and width
as
val linearLayout = LinearLayout(requireActivity())
val lp: LinearLayout.LayoutParams = (ParentView)findViewById(R.id.parent_view);
val left_margin: Int = 3 //e.g
val top_margin = 0
val right_margin: Int = 3 //e.g
val bottom_margin = 0
lp.setMargins(left_margin, top_margin, right_margin, bottom_margin)
val size = Point()
val w = requireActivity().windowManager
w.defaultDisplay.getSize(size)
lp.width = size.x - 40
lp.height = size.y
linearLayout.layoutParams = lp
Or for better results, you can use the recycler view to achieve the same result in better handling and just to create a single instance of adapter, which will be completely dependent on you list you are passing to the recycler view. also less code work to handle and debug.

TextView integer numbers as drawable png

I would like to make TextView counter with my own graphic numbers. How can I do this? Example: integer number will be 14356, so I would like to show this number in TextView as a Drawable:
number1.png + number4.png + number3.png + number5.png + number6.png
Can somebody help me with some hint/tips how to code it? I don't need code, just some tips, or hints. I would like to code myself, just don't know how to start this problem.
Thank you very much.
Create a layout file with a LinearLayout and a few ImageViews in it:
<LinearLayout orientation="horizontal" ...>
<ImageView id="#+id/digit_1000" .../>
<ImageView id="#+id/digit_100" .../>
<ImageView id="#+id/digit_10" .../>
<ImageView id="#+id/digit_1" .../>
</LinearLayout>
Use as many ImageView as needed. Then when you update the number, you can do something like this:
private int[] digitDrawables = {R.drawable.digit_0, R.drawable.digit_1, ...}
private ImageView[] digitViews;
private void initialize() {
// Get image views from layout by ID.
int[] ids = new int[]{R.id.digit_1, R.id.digit_10, R.id.digit_100, R.id.digit_1000};
digitViews = new ImageView[4];
for (int i = 0; i < 4; i++) {
digitViews[i] = findViewById(ids[i]);
}
}
private void updateNumber(int number) {
String str = String.valueOf(number);
// Go over each digit in string, updating the image views with:
// -> digitViews[i].setImageResource(digitDrawables[digit]);
// Hide the image views that you don't need with:
// -> digitViews[i].setVisibility(View.GONE);
}
You can start from there.
SO I would suggest doing this with Constraint Layout Horizontal Chain(in packed Configuration) of Image Views.
In the code you can just update a counter within a loop with 1-sec delay if its count down timmer else do it the way you indent to(game points) and if conditions checking if the new number changes 10's place, 100's place ....100000's place and update accordingly.

How to set layout_columnWeight for a GridLayout child when added programmatically?

I'd like to use a GridLayout (not GridView) as board for a game like chess or checkers. As I'm a little reluctant to use an xml file with 64 child Views, I've tried adding them programmatically.
To keep things simple, I started with using TextViews as child Views for the GridLayout.
My problem is that the Views are not distributed evenly, and that I don't know how to get an even distribution in my java code. There is no method like "setWeight()" for setting layout_columnWeight and layout_rowWeight.
At present, this is my activity_dynamic_grid_layout.xml:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:layout_width="match_parent"
android:layout_height="80dp"
android:id="#+id/ivLogo"
android:background="#ff0000"
android:layout_alignParentTop="true"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"/>
<android.support.v7.widget.GridLayout
android:id="#+id/grid_layout"
android:background="#004080"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_below="#+id/ivLogo"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:layout_marginTop="36dp">
</android.support.v7.widget.GridLayout>
I've set the GridLayout width and height to match_parent here, but I'm changing them at runtime using a ViewTreeObserver.OnPreDrawListener in order to get a square board. This works, the colored background is showing a square space as intended.
My onCreate()
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_dynamic_grid_layout);
GridLayout gl = (GridLayout) findViewById(R.id.grid_layout);
gl.setColumnCount(8);
gl.setRowCount(8);
for(int i=0; i<gl.getRowCount(); i++)
{
GridLayout.Spec rowSpec = GridLayout.spec(i, 1, GridLayout.FILL);
for(int j=0;j<gl.getColumnCount();j++)
{
GridLayout.Spec colSpec = GridLayout.spec(j,1, GridLayout.FILL);
TextView tvChild = new TextView(this);
tvChild.setText("[ " + i + " | " + j + " ]");
tvChild.setTextSize(18f);
tvChild.setTextColor(Color.WHITE);
tvChild.setGravity(Gravity.CENTER);
GridLayout.LayoutParams myGLP = new GridLayout.LayoutParams();
myGLP.rowSpec = rowSpec;
myGLP.columnSpec = colSpec;
gl.addView(tvChild, myGLP );
}
}
final View rootView = findViewById(R.id.dynamic_root);
rootView.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener()
{
#Override
public boolean onPreDraw()
{
int w = rootView.getMeasuredWidth();
int h = rootView.getMeasuredHeight();
int min = Math.min(w, h);
ViewGroup.LayoutParams lp = gl.getLayoutParams();
lp.width = min - min % 9;
lp.height = lp.width;
gl.setLayoutParams(lp);
rootView.getViewTreeObserver().removeOnPreDrawListener(this);
return true;
}
});
}
What I've tried already:
I put one TextView child in the layout file and tried to copy the layout_columnWeight and layout_rowWeight from its GridLayout.LayoutParams:
<android.support.v7.widget.GridLayout
...>
<TextView
android:id="#+id/clone_my_params"
android:text="[ 0 | 0 ]"
android:textColor="#ffffff"
android:textSize="18sp"
app:layout_column="0"
app:layout_row="0"
app:layout_columnWeight="1"
app:layout_rowWeight="1"
/>
</android.support.v7.widget.GridLayout>
Additional code in onCreate(), before the double loop:
TextView v = (TextView)gl.findViewById(R.id.clone_my_params);
v.setGravity(Gravity.CENTER);
GridLayout.LayoutParams gridLayoutParamsToCopy = new GridLayout.LayoutParams(v.getLayoutParams());
Inside the loop, I skipped (i,j) = (0,0) and changed
GridLayout.LayoutParams myGLP = new GridLayout.LayoutParams();
to
GridLayout.LayoutParams myGLP = new GridLayout.LayoutParams(gridLayoutParamsToCopy);
Before the change, all elements were in the upper left corner, the excess space was given to the last row/ column. After the change, the first row/ column had the excess space, no change for the other elements.
Calling gl.invalidate() and/or gl.requestLayout() after the double loop had no effect.
So it seems that I did not manage to set the desired weight by using the copy constructor.
-Here you are ! :>
Button button = new Button(this);
GridLayout.LayoutParams param= new GridLayout.LayoutParams(GridLayout.spec(
GridLayout.UNDEFINED,GridLayout.FILL,1f),
GridLayout.spec(GridLayout.UNDEFINED,GridLayout.FILL,1f));
param.height = 0;
param.width = 0;
button.setLayoutParams(param);
To set the weight on the children of your Gridlayout use one of the spec()(like this one) methods that takes a float value.
Another approach would be a either make a custom View(in which case you'll need to manually draw the pieces) or a custom ViewGroup(in which case the custom ViewGroup will just take care of the pieces positioning, this will be appropriate if you plan to have more complex view hierarchies than a simple TextView).

Paginating text in Android

I am writing a simple text/eBook viewer for Android, so I have used a TextView to show the HTML formatted text to the users, so they can browse the text in pages by going back and forth. But my problem is that I can not paginate the text in Android.
I can not (or I don't know how to) get appropriate feedback from the line-breaking and page-breaking algorithms in which TextView uses to break text into lines and pages. Thus, I can not understand where the content ends in the actual display, so that I continue from the remaining in the next page. I want to find way to overcome this problem.
If I know what is the last character painted on the screen, I can easily put enough characters to fill a screen, and knowing where tha actual painting was finished, I can continue at the next page. Is this possible? How?
Similar questions have been asked several times on StackOverflow, but no satisfactory answer was provided. These are just a few of them:
How to paginate long text into pages in Android?
Ebook reader pagination issue in android
Paginate text based on rendered text size
There was a single answer, which seems to work, but it is slow. It adds characters and lines until the page is filled. I don't think this is a good way to do page breaking:
How to break styled text into pages in Android?
Rather than this question, it happens that PageTurner eBook reader does it mostly right, although it is somehow slow.
https://github.com/nightwhistler/pageturner
PS: I am not confined to TextView, and I know line breaking and page breaking algorithms can be quite complex (as in TeX), so I am not looking for an optimal answer, but rather a reasonably fast solution that can be usable by the users.
Update: This seems to be a good start for getting the right answer:
Is there a way of retrieving a TextView's visible line count or range?
Answer: After completing text layout, it is possible to find out the visible text:
ViewTreeObserver vto = txtViewEx.getViewTreeObserver();
vto.addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
ViewTreeObserver obs = txtViewEx.getViewTreeObserver();
obs.removeOnGlobalLayoutListener(this);
height = txtViewEx.getHeight();
scrollY = txtViewEx.getScrollY();
Layout layout = txtViewEx.getLayout();
firstVisibleLineNumber = layout.getLineForVertical(scrollY);
lastVisibleLineNumber = layout.getLineForVertical(height+scrollY);
}
});
NEW ANSWER
PagedTextView library (in Kotlin) summarises the below lying algorithm by extending Android TextView. The sample app demonstrates the usage of the library.
Setup
dependencies {
implementation 'com.github.onikx:pagedtextview:0.1.3'
}
Usage
<com.onik.pagedtextview.PagedTextView
android:layout_width="match_parent"
android:layout_height="match_parent" />
OLD ANSWER
The algorithm below implements text pagination in separation of TextView itself lacking simultaneous dynamic change of both the TextView attributes and algorithm configuration parameters.
Background
What we know about text processing within TextView is that it properly breaks a text by lines according to the width of a view. Looking at the TextView's sources we can see that the text processing is done by the Layout class. So we can make use of the work the Layout class does for us and utilizing its methods do pagination.
Problem
The problem with TextView is that the visible part of text might be cut vertically somewhere at the middle of the last visible line. Regarding said, we should break a new page when the last line that fully fits into a view's height is met.
Algorithm
We iterate through the lines of text and check if the line's bottom exceeds the view's height;
If so, we break a new page and calculate a new value for the cumulative height to compare the following lines' bottom with (see the implementation). The new value is defined as top value (red line in the picture below) of the line that hasn't fit into the previous page + TextView's height.
Implementation
public class Pagination {
private final boolean mIncludePad;
private final int mWidth;
private final int mHeight;
private final float mSpacingMult;
private final float mSpacingAdd;
private final CharSequence mText;
private final TextPaint mPaint;
private final List<CharSequence> mPages;
public Pagination(CharSequence text, int pageW, int pageH, TextPaint paint, float spacingMult, float spacingAdd, boolean inclidePad) {
this.mText = text;
this.mWidth = pageW;
this.mHeight = pageH;
this.mPaint = paint;
this.mSpacingMult = spacingMult;
this.mSpacingAdd = spacingAdd;
this.mIncludePad = inclidePad;
this.mPages = new ArrayList<>();
layout();
}
private void layout() {
final StaticLayout layout = new StaticLayout(mText, mPaint, mWidth, Layout.Alignment.ALIGN_NORMAL, mSpacingMult, mSpacingAdd, mIncludePad);
final int lines = layout.getLineCount();
final CharSequence text = layout.getText();
int startOffset = 0;
int height = mHeight;
for (int i = 0; i < lines; i++) {
if (height < layout.getLineBottom(i)) {
// When the layout height has been exceeded
addPage(text.subSequence(startOffset, layout.getLineStart(i)));
startOffset = layout.getLineStart(i);
height = layout.getLineTop(i) + mHeight;
}
if (i == lines - 1) {
// Put the rest of the text into the last page
addPage(text.subSequence(startOffset, layout.getLineEnd(i)));
return;
}
}
}
private void addPage(CharSequence text) {
mPages.add(text);
}
public int size() {
return mPages.size();
}
public CharSequence get(int index) {
return (index >= 0 && index < mPages.size()) ? mPages.get(index) : null;
}
}
Note 1
The algorithm works not just for TextView (Pagination class uses TextView's parameters in the implementation above). You may pass any set of parameters StaticLayout accepts and later use the paginated layouts to draw text on Canvas/Bitmap/PdfDocument.
You can also use Spannable as yourText parameter for different fonts as well as Html-formatted strings (like in the sample below).
Note 2
When all text has the same font size, all lines have equal height. In that case you might want to consider further optimization of the algorithm by calculating an amount of lines that fits into a single page and jumping to the proper line at each loop iteration.
Sample
The sample below paginates a string containing both html and Spanned text.
public class PaginationActivity extends Activity {
private TextView mTextView;
private Pagination mPagination;
private CharSequence mText;
private int mCurrentIndex = 0;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_pagination);
mTextView = (TextView) findViewById(R.id.tv);
Spanned htmlString = Html.fromHtml(getString(R.string.html_string));
Spannable spanString = new SpannableString(getString(R.string.long_string));
spanString.setSpan(new ForegroundColorSpan(Color.BLUE), 0, 24, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
spanString.setSpan(new RelativeSizeSpan(2f), 0, 24, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
spanString.setSpan(new StyleSpan(Typeface.MONOSPACE.getStyle()), 0, 24, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
spanString.setSpan(new ForegroundColorSpan(Color.BLUE), 700, spanString.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
spanString.setSpan(new RelativeSizeSpan(2f), 700, spanString.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
spanString.setSpan(new StyleSpan(Typeface.MONOSPACE.getStyle()), 700, spanString.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
mText = TextUtils.concat(htmlString, spanString);
mTextView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
// Removing layout listener to avoid multiple calls
mTextView.getViewTreeObserver().removeOnGlobalLayoutListener(this);
mPagination = new Pagination(mText,
mTextView.getWidth(),
mTextView.getHeight(),
mTextView.getPaint(),
mTextView.getLineSpacingMultiplier(),
mTextView.getLineSpacingExtra(),
mTextView.getIncludeFontPadding());
update();
}
});
findViewById(R.id.btn_back).setOnClickListener(v -> {
mCurrentIndex = (mCurrentIndex > 0) ? mCurrentIndex - 1 : 0;
update();
});
findViewById(R.id.btn_forward).setOnClickListener(v -> {
mCurrentIndex = (mCurrentIndex < mPagination.size() - 1) ? mCurrentIndex + 1 : mPagination.size() - 1;
update();
});
}
private void update() {
final CharSequence text = mPagination.get(mCurrentIndex);
if(text != null) mTextView.setText(text);
}
}
Activity's layout:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingLeft="#dimen/activity_horizontal_margin"
android:paddingRight="#dimen/activity_horizontal_margin"
android:paddingTop="#dimen/activity_vertical_margin"
android:paddingBottom="#dimen/activity_vertical_margin" >
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<Button
android:id="#+id/btn_back"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:background="#android:color/transparent"/>
<Button
android:id="#+id/btn_forward"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:background="#android:color/transparent"/>
</LinearLayout>
<TextView
android:id="#+id/tv"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</RelativeLayout>
Screenshot:
Take a look at my demo project.
The "magic" is in this code:
mTextView.setText(mText);
int height = mTextView.getHeight();
int scrollY = mTextView.getScrollY();
Layout layout = mTextView.getLayout();
int firstVisibleLineNumber = layout.getLineForVertical(scrollY);
int lastVisibleLineNumber = layout.getLineForVertical(height + scrollY);
//check is latest line fully visible
if (mTextView.getHeight() < layout.getLineBottom(lastVisibleLineNumber)) {
lastVisibleLineNumber--;
}
int start = pageStartSymbol + mTextView.getLayout().getLineStart(firstVisibleLineNumber);
int end = pageStartSymbol + mTextView.getLayout().getLineEnd(lastVisibleLineNumber);
String displayedText = mText.substring(start, end);
//correct visible text
mTextView.setText(displayedText);
Surprisingly finding libraries for Pagination is difficult. I think it's better to use another Android UI element besides TextView. How about WebView?
An example # android-webview-example.
Code snippet:
webView = (WebView) findViewById(R.id.webView1);
String customHtml = "<html><body><h1>Hello, WebView</h1></body></html>";
webView.loadData(customHtml, "text/html", "UTF-8");
Note: This simply loads data onto a WebView, similar to a web browser. But let's not stop with just this idea. Add this UI to using pagination by WebViewClient onPageFinished . Please read on SO link # html-book-like-pagination.
Code snippet from one of the best answer by Dan:
mWebView.setWebViewClient(new WebViewClient() {
public void onPageFinished(WebView view, String url) {
...
mWebView.loadUrl("...");
}
});
Notes:
The code loads more data upon page scroll.
On the same webpage, there is a posted answer by Engin Kurutepe to set measurements for the WebView. This is necessary for specifing a page in pagination.
I have not implemented pagination but I think this is a good start and shows promise, should be fast. As you can see, there are developers that have implemented this feature.

Programatically place TextViews underneath eachother in a RelativeLayout

Problem
I've been trying to place a couple of TextViews underneath eachother, but I can't seem to get it working. They always overlap eachother in stead of getting placed beneath eachother.
What have I tried
I've tried messing with gravity (which doesn't work with a RelativeLayout), and all sorts of layout parameters. I've concluded that the best solution for me would be to use the RelativeLayout.BELOW parameter. The only problem is I'm trying to find the id of the previous TextView.
I assign the id's for the TextViews using the iterator. It seems I can't use the iterator - 1 to count as an id (even though other answers on SO suggest this works). I've even tried assigning the current TextView to a "previous_tv" variable to use in the next iteration.
I tried finding the TextView I just placed using this.findViewByID and the correct iterator value for the id, this also did not work.
Code
I'm trying to figure out what I should place as an id for my parameter rule. This is the code I'm reffering to -edit, placed full code as per request-:
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
this.requestWindowFeature(Window.FEATURE_NO_TITLE); //Don't show application name on the top of the screen (valuable space)
setContentView(R.layout.activity_task_play); //Set which layout file to use for this activity
//Get the Task that was sent by TaskActivity
this.task = (Task) this.getIntent().getSerializableExtra("TASK_OBJECT");
//Get the Sums for this Task
this.sums = this.task.getSums();
//GridView setup
this.gridview = (GridView) this.findViewById(R.id.gridView_task_play);
ArrayAdapter<String> adapter = this.task.getArrayAdapterForGridView(this);
this.gridview.setAdapter(adapter);
//TextView setup
RelativeLayout layout = (RelativeLayout) this.findViewById(R.id.activity_task_play_relative);
for(int i = 0; i < this.sums.length; i++)
{
//Layout parameters
RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.WRAP_CONTENT, RelativeLayout.LayoutParams.WRAP_CONTENT);
params.addRule(RelativeLayout.CENTER_HORIZONTAL);
//This variable counts how many times the placeholder text is skipped and a operator (+-/ etc.) is placed in the sum_text.
//This is usefull for placing the correct placeholder for each number in the sum (a, b, c etc.)
int times_skipped = 0;
TextView tv = new TextView(this);
String sum_text = "";
for(int j = 0; j < this.sums[i].getVariables().length; j++)
{
if(this.isParsable(this.sums[i].getVariables()[j]))
{
sum_text += TaskPlayActivity.PLACEHOLDERS[(j - times_skipped)] + " ";
}
else
{
sum_text += this.sums[i].getVariables()[j] + " ";
times_skipped++;
}
}
if(i > 0)
{
params.addRule(RelativeLayout.BELOW, i - 1);
}
tv.setId(i);
tv.setText(sum_text + "= " + this.sums[i].getAnswer());
tv.setTextColor(TaskPlayActivity.COLOURS[i]);
tv.setTextSize(25);
tv.setLayoutParams(params);
layout.addView(tv);
}
}
XMLAdded the XML. I'm not very good in layouts so it could very well be that the fault resides here.
<RelativeLayout
android:id="#+id/activity_task_play_relative"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<RelativeLayout
android:layout_width="wrap_content"
android:layout_height="220dip"
android:layout_alignParentBottom="true">
<GridView
android:id="#+id/gridView_task_play"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:background="#EFEFEF"
android:horizontalSpacing="1dp"
android:numColumns="3"
android:paddingTop="1dp"
android:verticalSpacing="1dp" >
</GridView>
</RelativeLayout>
</RelativeLayout>
Any other suggestions are welcome as well, though I would prefer keeping a RelativeLayout. Thanks in advance.
The documentation of View#setId says the identifier should be a positive number so you should make sure not to use zero as identifier value.
Also you have to create a new LayoutParams instance for each TextView. As it is all your TextViews share the same LayoutParams object and changes to that one object affect all TextViews.
And you could use View#generateViewId to generate an ID and remember the ID of the last iteration.
Seems like you are calling this code in a loop. In that case just put i - 1 (previous view id) as id for the rule.

Categories

Resources