Android sliding tabs - 2 line text on tabs - java

I came across this image online and I was wondering: is it possible to have a two line text view for one of my tabs within my sliding tabs project?
Hello tab error
DeafultTabView code
protected TextView createDefaultTabView(Context context) {
TextView textView = new TextView(context);
textView.setGravity(Gravity.CENTER);
textView.setTextSize(TypedValue.COMPLEX_UNIT_SP, TAB_VIEW_TEXT_SIZE_SP);
textView.setTypeface(Typeface.DEFAULT_BOLD);
textView.setTextColor(context.getResources().getColor(R.color.black));
WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
Display display = wm.getDefaultDisplay();
Point size = new Point();
display.getSize(size);
textView.setWidth(size.x / 2); // (size.x / number of textviews);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
// If we're running on Honeycomb or newer, then we can use the Theme's
// selectableItemBackground to ensure that the View has a pressed state
TypedValue outValue = new TypedValue();
getContext().getTheme().resolveAttribute(android.R.attr.selectableItemBackground,
outValue, true);
textView.setBackgroundResource(outValue.resourceId);
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
// If we're running on ICS or newer, enable all-caps to match the Action Bar tab style
textView.setAllCaps(true);
}
int padding = (int) (TAB_VIEW_PADDING_DIPS * getResources().getDisplayMetrics().density);
textView.setPadding(padding, padding, padding, padding);
return textView;
}

Use this in the TextView returned from createDefaultTabView() method
myTextView.setText(Html.fromHtml("Line one <br/>Line two"));
set text like this (Generic condition)
private void setCustomText(TextView textView, String[] msg, boolean isSingleLine){
if(isSingleLine)
textView.setText(msg[0]);
else
textView.setText(TextUtils.join("<br/>", msg));
}

Related

How to Auto Size text by height programmaticaly?

I have a vertical LinearLayout with a height of 32dp and i want to fit two TextView of 16dp each. The length of the text is different and i want it to always nicely fit in a single line.
I have tried with an AutoTextView and with a classic TextView + AutoSizing by TextViewCompat but none of these really work. My text shrink a bit but always finish to be crop at the bottom.
Here the base code
LinearLayout adressTextLayout = new LinearLayout(activity);
adressTextLayout.setOrientation(LinearLayout.VERTICAL);
adressTextLayout.setLayoutParams(new TableLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
//icon_size_big = 32dp
(int) (activity.getResources().getDimension(R.dimen.icon_size_big))));
boolean noAdress = true;
//First line with Country, Region and department
if(!mondialAdress.isEmpty()){
noAdress = false;
TextView tx = Utils.createAutoFitTextView(activity, mondialAdress, true, (int) (activity.getResources().getDimension(R.dimen.icon_size_big)/2));
tx.setTypeface(null, Typeface.ITALIC);
adressTextLayout.addView(tx);
}
//Second line with adress, postal code and city
if(!localAdress.isEmpty()){
noAdress = false;
TextView tx = Utils.createAutoFitTextView(activity, localAdress, true, (int) (activity.getResources().getDimension(R.dimen.icon_size_big)/2));
adressTextLayout.addView(tx);
}
//Default line in case we didn't found any adress
if(noAdress){
adressTextLayout.addView(Utils.createAutoFitTextView(activity, activity.getResources().getString(R.string.hint_provide_information), true, 0));
}
Here is the createAutoFitTextView() function with TextViewCompat
public static TextView createAutoFitTextView(Activity activity, String text, boolean singleLine, int maxHeight) {
TextView textView = new AutofitTextView(activity);
textView.setText(text);
//MATCH_PARENT by default
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, maxHeight > 0 ? maxHeight : ViewGroup.LayoutParams.MATCH_PARENT);
textView.setLayoutParams(params);
//Auto Sizing by TextViewCompat
TextViewCompat.setAutoSizeTextTypeWithDefaults(textView, TextViewCompat.AUTO_SIZE_TEXT_TYPE_UNIFORM);
if(singleLine){
textView.setSingleLine(true);
}
return textView;
}
Here the function with AutoFitTextView
public static AutofitTextView createAutoFitTextView(Activity activity, String text, boolean singleLine, int maxHeight) {
AutofitTextView textView = new AutofitTextView(activity);
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, maxHeight != -1 ? maxHeight : ViewGroup.LayoutParams.MATCH_PARENT);
textView.setLayoutParams(params);
textView.setText(text);
if(singleLine){
textView.setSingleLine(true);
}
return textView;
}
Here a the result :
With TextViewCompat
With AutoFillTextView:

HorizontalScrollView scrollTo doesn't scroll above original width of the view

I have a custom view containing an HorizontalScrollView. The width of the scroll view is match_parent, and the width of its children is initially programmatically set based on the value of an attribute of the custom view. At some point, the width of the children of the scroll view are updated (increased) programmatically. The problem is that, after the update, the scrollTo method is still unable to scroll above the original width value (the same for scrollBy).
The enclosing view (the custom one) have a left and right padding equal to half the screen, if this is relevant.
Example:
Initial HorizontalScrollView's children width: 1000;
HorizontalScrollView's parent width: 1080;
HorizontalScrollView's left/right padding = 540;
New HorizontalScrollView's children width: 2000;
scrollTo(1100, 0) = scrollTo(1000, 0); <---- here is the problem
Relevant code:
Custom view initialization:
private void init(AttributeSet attrs, int defStyle) {
inflate(getContext(), R.layout.timeline_view, this);
// Load attributes
final TypedArray a = getContext().obtainStyledAttributes(attrs, R.styleable.TimelineView, defStyle, 0);
m_timelineInitialLength = a.getDimension(R.styleable.TimelineView_timelineInitialLength, DEFAULT_LENGTH);
a.recycle();
DisplayMetrics displayMetrics = new DisplayMetrics();
((Activity) getContext()).getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);
LinearLayout timelineWidget = findViewById(R.id.timeline_widget);
int horizontalPadding = displayMetrics.widthPixels / 2;
timelineWidget.setPadding(horizontalPadding, 0, horizontalPadding, 0);
m_horizontalScrollView = findViewById(R.id.scroll_view);
m_horizontalScrollView.getViewTreeObserver().addOnScrollChangedListener(new ViewTreeObserver.OnScrollChangedListener() {
#Override
public void onScrollChanged() {
m_currentScrollX = m_horizontalScrollView.getScrollX();
}
});
m_currentScrollX = m_horizontalScrollView.getScrollX();
update(m_timelineInitialLength, m_timelineInitialStepWidth, m_currentScrollX);
}
Update and scrollTo (this method is called also at the end of the initialization. m_rulerView and m_timelineLayout are the children of the HorizontalScrollView):
private void update(float timelineLength, float timelineStepWidth, int currentScrollX) {
m_rulerView = findViewById(R.id.ruler);
m_rulerView.setIndicator(null);
m_rulerView.getLayoutParams().width = (int) timelineLength;
m_rulerView.requestLayout();
m_timelineLayout = findViewById(R.id.timeline);
m_timelineLayout.getLayoutParams().width = (int) timelineLength;
m_timelineLayout.requestLayout();
m_horizontalScrollView.scrollTo(currentScrollX, 0);
}
The problem seems to be that the scrolling happens before the children have been redrawn, before calling requestLayout only schedule the update of the view to be executed at some point in the future (the same for invalidate()).
I solved using a workaround, by executing scrollTo after some time:
new Handler().postDelayed(() -> ((Activity) getContext()).runOnUiThread(() -> m_horizontalScrollView.scrollTo(currentScrollX, 0)), 200);
I'm still open to better solutions.

How to know the max amount of ItemViews that can be visible in the RecyclerView without loading the data?

I have a paging algorithm for the RecyclerView, which if done Scroll, a scrolling listener triggers and loads more elements. Initially, I assigned a number of elements to load in the request to the server by default that is 20 for my RecyclerView, which has a fixed size (not wrap_content).
I need to know the number of items that can be visible in the width/height of the defined RecyclerView before loading the data, to determine the amount of items to be requested in the initial load, given that with 20 items on some devices is not enough to activate the listener of the scroll and load more elements.
This is without considering the extra properties of the view, such as padding, margin etc ...
The solution can be in Java Android or Xamarin Android (not Forms) C#.
Update:
For you to have a clue, i have tried this and it works for me, only if I call it when the size of RecyclerView is assigned, inside the OnLayoutChange:
public int GetMaxVisibleItemCountFromRecyclerView(RecyclerView recyclerView)
{
if (recyclerView == null) return 0;
int Width = recyclerView.Width;
int Height = recyclerView.Height;
if (Width == 0 || Height == 0)
return 0;
var layoutManager = recyclerView.GetLayoutManager() as GridLayoutManager;
if (layoutManager == null) return 0;
int widthRatio = Width / layoutManager.SpanCount;
int quantity = (Height / widthRatio) * layoutManager.SpanCount;
return quantity;
}
This solution only works for RecyclerViews that use the GridLayoutManager. I have other RecyclerViews with defined sizes that also use the paging algorithm with a LinearLayoutManager.
I need a similar solution, that works with any LayoutManager of the RecyclerView and does not have to be called inside the OnLayoutChange, is this possible?
Instead of trying to replicate the computations that RecyclerView does to compute layout, let the system do the work for you. The following example lets RecyclerView lay out one item and measurements are taken from that. The dummy item is not displayed and is used just for measurement.
The advantage of this method is that we don't have to replicate what RecyclerView does to measure items. All key measurements are taken into account including padding, margins and decorations.
The following sample shows how this can be accomplished for GridLayoutManager and LinearLayoutManager. StaggeredGridLayoutManager and FlexboxLayoutManager are special cases and aren't taken into account here.
Here is a short video showing the results of this demo app showing that just one page of items was loaded.
MainActivity.java
public class MainActivity extends AppCompatActivity {
private List<String> mItems = new ArrayList<>();
private RecyclerView mRecycler;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mRecycler = findViewById(R.id.recyclerView);
// Sample for vertical LinearLayoutManager.
LinearLayoutManager layoutManager = new LinearLayoutManager(this);
// Sample for GridLayoutManager with 4 spans. Each item comsumes 2 spans.
// GridLayoutManager layoutManager = new GridLayoutManager(this, 4);
// layoutManager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {
// #Override
// public int getSpanSize(int position) {
// return 2;
// }
// });
// Add single dummy item that will be measured but not be displayed.
mItems.add("Dummy item");
RecyclerViewAdapter mAdapter = new RecyclerViewAdapter(mItems);
mRecycler.setLayoutManager(layoutManager);
mRecycler.setAdapter(mAdapter);
// Take measurements in OnPreDraawListener(). This could also be accomplished with
// mRecyclerView.post(new Runnable()...)
mRecycler.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
#Override
public boolean onPreDraw() {
mRecycler.getViewTreeObserver().removeOnPreDrawListener(this);
// RecyclerView is laid out with single dummy entry. Get how many of this type
// of item can fit into the visible portion of the RecyclerView.
final int nItems = getInitialLoadCount(mRecycler);
Log.d(TAG, "<<<<Items per page=" + nItems);
// Don't need the dummy entry any more.
mItems.clear();
mRecycler.getAdapter().notifyDataSetChanged();
mItems = new ArrayList<>();
// Fake load...
loadInitialItems(nItems);
return false;
}
});
}
// Determine how many items will fill one screen of the RecyclerView. Call with the
// RecyclerView loaded with at least one item for measurement.
private int getInitialLoadCount(RecyclerView recyclerView) {
int itemsToLoad = 0;
RecyclerView.LayoutManager lm = recyclerView.getLayoutManager();
View firstChild = recyclerView.getChildAt(0);
if (lm instanceof LinearLayoutManager) {
Rect bounds = new Rect();
recyclerView.getDecoratedBoundsWithMargins(firstChild, bounds);
if (lm.canScrollVertically()) {
int recyclerHeightForItems = recyclerView.getHeight() - recyclerView.getPaddingTop()
- recyclerView.getPaddingBottom();
itemsToLoad = (recyclerHeightForItems + bounds.height() - 1) / bounds.height();
} else if (lm.canScrollHorizontally()) {
int recyclerWidthForItems = recyclerView.getWidth() - recyclerView.getPaddingLeft()
- recyclerView.getPaddingRight();
itemsToLoad = (recyclerWidthForItems + bounds.width() - 1) / bounds.width();
}
if (lm instanceof GridLayoutManager) {
// Adjust for GridLayoutManager. All items should to be the same number of spans.
GridLayoutManager glm = (GridLayoutManager) lm;
itemsToLoad *= glm.getSpanCount() / glm.getSpanSizeLookup().getSpanSize(0);
}
}
return itemsToLoad;
}
private void loadInitialItems(final int itemCount) {
// Simulate load of nItems...should be on non-UI thread.
new Thread(new Runnable() {
#Override
public void run() {
for (int i = 1; i <= itemCount; i++) {
sleep(250);
mItems.add("Item #" + i);
}
runOnUiThread(new Runnable() {
#Override
public void run() {
mRecycler.swapAdapter(new RecyclerViewAdapter(mItems), true);
}
});
}
}).start();
}
private static final String TAG = "MainActivity";
}
If your RecyclerView and your item width and height are defined in dp, then you should save their dimensions in dimen.xml. Then you can calculate how many items will fit like this:
float recyclerHeight = getResources().getDimension(R.dimen.recycler_height);
float itemHeight = getResources().getDimension(R.dimen.item_height);
int numOfItemsFit = (int) (recyclerHeight / itemHeight);
If your recyclerView isn't defined by dp, but the rest of the views in this layout are you can try to accomplish the same thing by decreasing the other views height from the total view height. You can check the total view height with:
public int getHeight() {
Display display = getWindowManager().getDefaultDisplay();
Point size = new Point();
display.getSize(size);
return size.y;
}
If you can't do the above you'll need to measure your RecyclerView inside of OnLayoutChange :( . You can do that with: recyclerView.getMeasuredHeight(); and recyclerView.getMeasuredWidth();

Android: How to load multiple pictures and sounds in an activity without a out of memory error

So I have an activity that is a gridview of HD images that are shrunk down to about 64x64 pixel boxes. I want to be able to get the image and then load them in the activity but I get an error that says that the Memory that it takes to load all of the pictures is a whole lot higher than the emulator/phone can handle.
I solved the problem by going into photoshop and making 64x64 images, but I've seen applications that run video and have hundreds of images in the current activity's view. How do they compile with all of that memory in one listview?
I have another application that is a animation that plays a full screen animation with 20 pictures - but it has the same memory bound error in Android Studio.
How can I load 20 pictures on a single activity without getting a out of memory error.
Code for Activity with GridView
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.wallpapers);
//**
//Gridview, onclick of icon will open up dialog
//allows to SAVE, SET, CANCEL - not in that order
GridView gridview = (GridView) findViewById(R.id.gridview);
gridview.setAdapter(new ImageAdapter(this));
gridview.setOnItemClickListener(new AdapterView.OnItemClickListener() {
public void onItemClick(AdapterView<?> parent, View v,
int position, long id) {
Toast.makeText(Wallpaper.this, "Setting BG to ..." + position,
Toast.LENGTH_SHORT).show();
}
});
}
Code for ImageAdapter
public class ImageAdapter extends BaseAdapter {
private Context mContext;
public ImageAdapter(Context c) {
mContext = c;
}
public int getCount() {
return mThumbIds.length;
}
public Object getItem(int position) {
return null;
}
public long getItemId(int position) {
return 0;
}
// create a new ImageView for each item referenced by the Adapter
public View getView(int position, View convertView, ViewGroup parent) {
ImageView imageView;
if (convertView == null) {
// if it's not recycled, initialize some attributes
imageView = new ImageView(mContext);
imageView.setLayoutParams(new GridView.LayoutParams(85, 85));
imageView.setScaleType(ImageView.ScaleType.CENTER_CROP);
imageView.setPadding(8, 8, 8, 8);
} else {
imageView = (ImageView) convertView;
}
imageView.setImageResource(mThumbIds[position]);
return imageView;
}
// references to our images
private Integer[] mThumbIds = {
R.raw.es_1_pix, R.raw.es1_c, R.raw.es2_c, R.raw.es_1_pix
//.raw.es_1_pix,R.raw.es_1_pix,R.raw.es_1_pix,R.raw.es_1_pix,R.raw.es_1_pix
//R.raw.es2_c, R.raw.es3_c, R.raw.es4_c,
//R.raw.es5_c, R.raw.es6_c, R.raw.es7_c
};
}
Not sure about the sound, but I know images need to use the Sample size with inJustDecodeBounds=true.
public int calculateInSampleSize(BitmapFactory.Options options) {
DisplayMetrics displayMetrics = cxt.getResources().getDisplayMetrics();
int reqWidth = displayMetrics.widthPixels;
final int height = options.outHeight;
final int width = options.outWidth;
double devCal2 = (height*1000)/width;
int reqHeight = (int) ((devCal2/1000)*reqWidth);
int inSampleSize = 1;
if (height > reqHeight || width > reqWidth) {
final int halfHeight = height / 2;
final int halfWidth = width / 2;
// Calculate the largest inSampleSize value that is a power of 2 and keeps both
// height and width larger than the requested height and width.
while ((halfHeight / inSampleSize) > reqHeight
&& (halfWidth / inSampleSize) > reqWidth) {
inSampleSize *= 2;
}
}
return inSampleSize;
}
public Bitmap createBitmap(){
BitmapFactory.Options options2 = new BitmapFactory.Options();
options2.inJustDecodeBounds = true;
options2.inDither=true;
BitmapFactory.decodeFile(cxt.getExternalFilesDir(filepath) +"/companylogo.png",options2);
options2.inSampleSize = calculateInSampleSize(options2);//=32
options2.inJustDecodeBounds = false;
return BitmapFactory.decodeFile(cxt.getExternalFilesDir(filepath) +"/companylogo.png",options2);
}
For reference please check http://developer.android.com/training/displaying-bitmaps/load-bitmap.html
There are a few things to consider.
The easier solution - use image management library, the are plenty of those. All of them do the following things:
Use WeakReference<Bitmap> everywhere instead of Bitmaps. WeakReference means System can clean images from the memory when there is not enough memory.
If you display 20 64*64 images than make sure you do not store 20 1024*1024 images in memory, resize it to 64*64 either on runtime or outside of app.
Generally speaking android can easily handle 20 average sized images(obviously not huge ones). If it rashes than there is something wrong with the code.
UPDATE after the code was posted: the images you have in resource file are huge, scale it down and you should be fine.
If you really need the full HD image you could calculate how much space you need and load them into the views one by one replacing the others with a placeholder image. Then after loading one image make sure the system clean's that image from memory. But like most people have said if its crashing from only 20 images it is probably something else.

Android Issue with activity context

So I have a helper class that reuses a lot of code through out the application, one of the methods is shown below:
public void setTitleTextSize(final int id){
infoButton = ((Activity) context).findViewById(R.id.info_button);
ViewTreeObserver customTitleScale = infoButton.getViewTreeObserver();
customTitleScale.addOnPreDrawListener(new OnPreDrawListener() {
#Override
public boolean onPreDraw() {
int infoWidth = infoButton.getMeasuredWidth();
if(infoWidth !=0){
if(Build.VERSION.SDK_INT >= 11.0){
TypedValue tv = new TypedValue();
((Activity) context).getTheme().resolveAttribute(android.R.attr.actionBarSize, tv, true);
actionBarWidth = ((Activity) context).getResources().getDimensionPixelSize(tv.resourceId);
}
DisplayMetrics metrics = new DisplayMetrics();
Display Screen = ((Activity) context).getWindowManager().getDefaultDisplay();
Screen.getMetrics(metrics);
int screenWidth = metrics.widthPixels;
int titleWidth = screenWidth - infoWidth - actionBarWidth;
TextView titleText = (TextView) ((Activity) context).findViewById(R.id.title_text);
titleText.setText(id);
TextPaint paint = titleText.getPaint();
Rect rect = new Rect();
String text = String.valueOf(titleText.getText());
int textLength = text.length();
paint.getTextBounds(text, 0, textLength, rect);
if(rect.width() > titleWidth){
float scale = (float) titleWidth / (float) rect.width();
float textSize = titleText.getTextSize();
float scaleSize = (float) (textSize * (scale*0.8));
titleText.setTextSize(TypedValue.COMPLEX_UNIT_PX, scaleSize);
}
infoButton.getViewTreeObserver().removeOnPreDrawListener(this);
}
return false;
}
});
}
I use this particular method on all of my activities.
The problem I've got is I don't want to display the infoButton on every activity but when I add View infoButton = findViewById(R.id.info_button); infoButton.setVisibility(View.GONE); to the activity, the screen is just black.
So I was thinking on how to do this and the only thing I can thing of is to pass a boolean into the method stating whether the info view is visible or not. I suppose I'd do an if statement saying `if true then display it, if false then don't but I can't figure out how to do this.
Any help would be amazing thanks.
Try to use method infoButton.getVisibility()
it will return you the integer to get visibility status of you widget.
I figured out the issue I was having, My logic wasn't correct for the if statement if(infoWidth !=0). The code wasn't running if the activity didn't have an info button because the infoWidth would always be zero. So I changed the logic to if(infoWidth !=0 || !displayInfoButton) which works perfectly.

Categories