I have a very simple task (at least I thought so) - make a 5 non-scrollable static tabs with icons and fragments within
I had spent full day on this task and I really was surprised , it seems that in modern Android API's it's hard to implement, cause all that I had found are useless:
PagerTabStrip - it's scrollable, can't use a fixed count of displayed tabs
ActionBar.TabListener - deprecated since API 21
FragmentTabHost - has some bug "Exception raised during rendering: No tab known for tag null"
SlidingTabLayout - sliding again :( can't use a fixed count of displayed tabs
Maybe I've missed something? Regards.
As I promised I made some demo project for you.
The most simple solution for you problem seemed LinearLayout and that is what I used and also the android's images.
First you need to create a file in values folder named attrs.xml. If you already have it then just add the stylable
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="SimpleTabIndicator">
<attr name="numberOfTabs" format="integer"/>
<attr name="indicatorColor" format="color"/>
</declare-styleable>
</resources>
Then create a class named SimpleTabIndicator
import android.content.Context;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.os.Build;
import android.util.AttributeSet;
import android.view.View;
public class SimpleTabIndicator extends View {
private static final String TAG = SimpleTabIndicator.class.getSimpleName();
private float density;
private int measuredHeight, measuredWidth;
private int mNumberOfTabs;
private Paint mIndicatorPaint;
private int mIndicatorColor = 0xFFFDE992;
private int currentTab = 1;
public SimpleTabIndicator(Context context) {
super(context);
init(null, 0);
}
public SimpleTabIndicator(Context context, AttributeSet attrs) {
super(context, attrs);
init(attrs, 0);
}
public SimpleTabIndicator(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(attrs, defStyleAttr);
}
private void init(AttributeSet attrs, int style) {
if (!isInEditMode() && Build.VERSION.SDK_INT >= 11) {
setLayerType(View.LAYER_TYPE_NONE, null);
}
Resources res = getResources();
density = res.getDisplayMetrics().density;
TypedArray typedArray = getContext().obtainStyledAttributes(attrs, R.styleable.SimpleTabIndicator, style, 0);
mIndicatorColor = typedArray.getColor(R.styleable.SimpleTabIndicator_indicatorColor, mIndicatorColor);
mNumberOfTabs = typedArray.getInt(R.styleable.SimpleTabIndicator_numberOfTabs, 1);
typedArray.recycle();
mIndicatorPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mIndicatorPaint.setStyle(Paint.Style.FILL);
mIndicatorPaint.setColor(mIndicatorColor);
}
public int getNumberOfTabs() {
return mNumberOfTabs;
}
public void setNumberOfTabs(int mNumberOfTabs) {
this.mNumberOfTabs = mNumberOfTabs;
invalidate();
}
public int getCurrentTab() {
return currentTab;
}
public void setCurrentTab(int currentTab) {
this.currentTab = currentTab;
invalidate();
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
measuredHeight = getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec);
measuredWidth = getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec);
setMeasuredDimension(measuredWidth, measuredHeight);
}
#Override
protected void onDraw(Canvas canvas) {
if (measuredHeight <= 0 || measuredWidth <= 0 || mNumberOfTabs == 0) {
return; // Not much we can draw :(
}
int length = measuredWidth / mNumberOfTabs;
int startX = (currentTab - 1) * length;
canvas.drawRect(startX, 0, startX + length, measuredHeight, mIndicatorPaint);
}
}
Next up is the ZoomOutPageTransformer. This guy is "borrowed" from Google.
import android.support.v4.view.ViewPager;
import android.view.View;
public class ZoomOutPageTransformer implements ViewPager.PageTransformer {
private static final float MIN_SCALE = 0.85f;
private static final float MIN_ALPHA = 0.5f;
public void transformPage(View view, float position) {
int pageWidth = view.getWidth();
int pageHeight = view.getHeight();
if (position < -1) { // [-Infinity,-1)
// This page is way off-screen to the left.
view.setAlpha(0);
}
else if (position <= 1) { // [-1,1]
// Modify the default slide transition to shrink the page as well
float scaleFactor = Math.max(MIN_SCALE, 1 - Math.abs(position));
float vertMargin = pageHeight * (1 - scaleFactor) / 2;
float horzMargin = pageWidth * (1 - scaleFactor) / 2;
if (position < 0) {
view.setTranslationX(horzMargin - vertMargin / 2);
}
else {
view.setTranslationX(-horzMargin + vertMargin / 2);
}
// Scale the page down (between MIN_SCALE and 1)
view.setScaleX(scaleFactor);
view.setScaleY(scaleFactor);
// Fade the page relative to its size.
view.setAlpha(MIN_ALPHA + (scaleFactor - MIN_SCALE) / (1 - MIN_SCALE) * (1 - MIN_ALPHA));
}
else { // (1,+Infinity]
// This page is way off-screen to the right.
view.setAlpha(0);
}
}
}
Next make a layout named dummy_fragment_layout.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="#+id/fragmentNumber"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:gravity="center_horizontal"
android:text="1"
android:textSize="50sp"
android:textStyle="bold"/>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_above="#id/fragmentNumber"
android:gravity="center_horizontal"
android:text="Fragment"
android:textSize="30sp"/>
</RelativeLayout>
Now the main activity activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#808080"
android:orientation="vertical"
>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="?actionBarSize"
android:background="#26292E"
android:orientation="horizontal">
<ImageView
android:id="#+id/tab1"
android:layout_width="0dip"
android:layout_height="wrap_content"
android:layout_weight="1"
android:adjustViewBounds="true"
android:onClick="onTabSelected"
android:padding="10dp"
android:src="#android:drawable/btn_radio"
android:tag="1"/>
<ImageView
android:id="#+id/tab2"
android:layout_width="0dip"
android:layout_height="wrap_content"
android:layout_weight="1"
android:adjustViewBounds="true"
android:onClick="onTabSelected"
android:padding="10dp"
android:src="#android:drawable/btn_star"
android:tag="2"/>
<ImageView
android:id="#+id/tab3"
android:layout_width="0dip"
android:layout_height="wrap_content"
android:layout_weight="1"
android:adjustViewBounds="true"
android:onClick="onTabSelected"
android:padding="10dp"
android:src="#android:drawable/btn_star"
android:tag="3"/>
<ImageView
android:id="#+id/tab4"
android:layout_width="0dip"
android:layout_height="wrap_content"
android:layout_weight="1"
android:adjustViewBounds="true"
android:onClick="onTabSelected"
android:padding="10dp"
android:src="#android:drawable/btn_star"
android:tag="4"/>
<ImageView
android:id="#+id/tab5"
android:layout_width="0dip"
android:layout_height="wrap_content"
android:layout_weight="1"
android:adjustViewBounds="true"
android:onClick="onTabSelected"
android:padding="10dp"
android:src="#android:drawable/btn_star"
android:tag="5"/>
</LinearLayout>
<test.kseneman.si.test.SimpleTabIndicator
android:id="#+id/tabIndicator"
android:layout_width="match_parent"
android:layout_height="2dp"
android:background="#26292E"
app:indicatorColor="#FFFDE992"
app:numberOfTabs="5"/>
<android.support.v4.view.ViewPager
android:id="#+id/pager"
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</LinearLayout>
And finally the MainActivity
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentStatePagerAdapter;
import android.support.v4.view.ViewPager;
import android.support.v7.app.ActionBarActivity;
import android.util.Log;
import android.view.View;
import android.widget.ImageView;
public class MainActivity extends ActionBarActivity {
private ViewPager mPager;
private DummyFragmentsAdapter mPagerAdapter;
private SimpleTabIndicator tabIndicator;
private ImageView selectedImageView;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mPagerAdapter = new DummyFragmentsAdapter(getSupportFragmentManager());
tabIndicator = (SimpleTabIndicator) findViewById(R.id.tabIndicator);
// Default state
selectedImageView = (ImageView) findViewById(R.id.tab1);
mPager = (ViewPager) findViewById(R.id.pager);
mPager.setAdapter(mPagerAdapter);
mPager.setPageTransformer(false, new ZoomOutPageTransformer());
}
public void onTabSelected(View v) {
// Sanity check
if (v == null || !(v instanceof ImageView) || v.getTag() == null) {
return;
}
int postion = Integer.valueOf((String) v.getTag());
Log.d("onTabSelected", "postion: " + postion);
if (postion == mPager.getCurrentItem() + 1) {
// The same selected, do nothing?
return;
}
// Change selected images
selectedImageView.setImageResource(android.R.drawable.btn_star);
selectedImageView = (ImageView) v;
selectedImageView.setImageResource(android.R.drawable.btn_radio);
mPager.setCurrentItem(postion - 1); // They start at 0
tabIndicator.setCurrentTab(postion);
}
private class DummyFragmentsAdapter extends FragmentStatePagerAdapter {
public DummyFragmentsAdapter(FragmentManager fm) {
super(fm);
}
#Override
public Fragment getItem(int position) {
DummyFragment fragment = new DummyFragment();
Bundle b = new Bundle();
b.putInt(DummyFragment.EXTRA_FRAGMENT_NUMBER, position);
fragment.setArguments(b);
return fragment;
}
#Override
public int getCount() {
return 5;
}
}
}
A complete project zip is available here
Here is what it looks like
use the new Tool
alternatine to actionbar (toolbar)
https://developer.android.com/reference/android/widget/Toolbar.html
Related
Well
I m just learning custom viewgroups in android so i have made a simple viewgroup. i named it LearnLayout
i have custom layoutparam in it, called as LearnLayoutParams
Problem
when i use custom attributes on children. those attributes are not being parsed in LearnLayoutParams adn i get default values
Code
here are my code files
LearnLayout.java
this is the cusom viewgroup class file
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Color;
import android.os.Build;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.RequiresApi;
import com.test.learnviewpagertransformations.MainActivity;
import com.test.learnviewpagertransformations.R;
public class LearnLayout extends ViewGroup {
private static final String TAG = MainActivity.TAG;
public LearnLayout(Context context) {
super(context);
}
public LearnLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}
public LearnLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int maxWidth = 0;
int maxHeight = 0;
for(int i=0; i<getChildCount(); i++){
View childView = getChildAt(i);
if(childView.getVisibility() != GONE){
measureChildWithMargins(childView, widthMeasureSpec, 0, heightMeasureSpec, 0);
final LearnLayoutParams lp = (LearnLayoutParams) childView.getLayoutParams();
int childWidth = lp.leftMargin + childView.getMeasuredWidth() + lp.rightMargin;
int childHeight = lp.topMargin + childView.getMeasuredHeight() + lp.bottomMargin;
maxWidth = Math.max(childWidth, maxWidth);
maxHeight = maxHeight + childHeight;
}
}
setMeasuredDimension(maxWidth, maxHeight);
}
#Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
int fromLeft = getPaddingLeft();
int fromTop = getPaddingTop();
int fromRight = right - left - getPaddingRight();
int fromBottom = bottom - top - getPaddingBottom();
for(int i=0; i<getChildCount(); i++){
View childView = getChildAt(i);
if (childView.getVisibility() != GONE) {
final LearnLayoutParams lp = (LearnLayoutParams) childView.getLayoutParams();
childView.layout(
fromLeft + lp.leftMargin,
fromTop + lp.topMargin,
fromRight - lp.rightMargin,
fromTop + lp.topMargin + lp.height
);
childView.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View view) {
Log.i(TAG, "onLayout: " + lp.backgroundColor);
}
});
childView.setBackgroundColor(getColor(lp.backgroundColor));
fromTop = fromTop + childView.getMeasuredHeight() + lp.topMargin + lp.bottomMargin;
}
}
}
#Override
public LearnLayoutParams generateLayoutParams(AttributeSet attrs) {
return new LearnLayoutParams(getContext(), attrs);
}
#Override
protected LearnLayoutParams generateDefaultLayoutParams() {
return new LearnLayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
}
#Override
protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
return new LearnLayoutParams(p);
}
#Override
protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
return p instanceof LearnLayoutParams;
}
public static class LearnLayoutParams extends MarginLayoutParams {
public int backgroundColor = 0;
public LearnLayoutParams(Context c, AttributeSet attrs) {
super(c, attrs);
TypedArray a = c.obtainStyledAttributes(R.styleable.LearnLayoutLP);
backgroundColor = a.getInt(R.styleable.LearnLayoutLP_layout_background_color, backgroundColor);
Log.i(TAG, "LayoutParams: " + backgroundColor);
a.recycle();
}
public LearnLayoutParams(int width, int height) {
super(width, height);
}
public LearnLayoutParams(ViewGroup.LayoutParams source) {
super(source);
}
}
private int getColor(int backgroundColor){
switch (backgroundColor){
case 0:
return Color.RED;
case 1:
return Color.BLUE;
case 2:
return Color.GREEN;
default:
return Color.BLACK;
}
}
}
activity_learn_layout.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="vertical"
android:gravity="center"
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.test.learnviewpagertransformations.views.LearnLayout
android:background="#99ff99"
android:layout_width="wrap_content"
android:layout_height="wrap_content" >
<View
app:layout_background_color="red"
android:layout_margin="50dp"
android:layout_width="100dp"
android:layout_height="100dp" />
<LinearLayout
android:id="#+id/testView"
android:gravity="center"
app:layout_background_color="blue"
android:layout_margin="50dp"
android:layout_width="100dp"
android:layout_height="100dp">
<TextView
android:textStyle="bold"
android:textColor="#fff"
android:text="Test"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</LinearLayout>
<View
app:layout_background_color="green"
android:layout_margin="50dp"
android:layout_width="100dp"
android:layout_height="100dp" />
</com.test.learnviewpagertransformations.views.LearnLayout>
<!--<com.test.learnviewpagertransformations.views.CustomLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<!– put first view to left. –>
<TextView
android:background="#drawable/filled_box"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_position="left"
android:layout_gravity="fill_vertical|center_horizontal"
android:text="l1"/>
<!– stack second view to left. –>
<TextView
android:background="#drawable/filled_box"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_position="left"
android:layout_gravity="fill_vertical|center_horizontal"
android:text="l2"/>
<!– also put a view on the right. –>
<TextView
android:background="#drawable/filled_box"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_position="right"
android:layout_gravity="fill_vertical|center_horizontal"
android:text="r1"/>
<!– by default views go in the middle; use fill vertical gravity –>
<TextView
android:background="#drawable/green"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="fill_vertical|center_horizontal"
android:text="fill-vert"/>
<!– by default views go in the middle; use fill horizontal gravity –>
<TextView
android:background="#drawable/green"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical|fill_horizontal"
android:text="fill-horiz"/>
<!– by default views go in the middle; use top-left gravity –>
<TextView
android:background="#drawable/blue"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="top|left"
android:text="top-left"/>
<!– by default views go in the middle; use center gravity –>
<TextView
android:background="#drawable/blue"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:text="center"/>
<!– by default views go in the middle; use bottom-right –>
<TextView
android:background="#drawable/blue"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|right"
android:text="bottom-right"/>
</com.test.learnviewpagertransformations.views.CustomLayout>-->
</LinearLayout>
attrs.xml
custom attributes that i have made for my layoutparams
<declare-styleable name="LearnLayoutLP">
<attr name="layout_background_color">
<enum name="red" value="0" />
<enum name="blue" value="1" />
<enum name="green" value="2" />
</attr>
</declare-styleable>
What i think
there is something i m missing in onLayout and onMeasure and because of that i m not getting the correct values of custom attributes. If you check my custom layoutparams class even at that place i dont get the values i have used all i get in logs is default value that it 0
Request
Please share whatever you have in your mind
I ll gladly welcome any amendments and improvements
_/\_
Want to create the view pager same as following UI, applied custom transformer but not working.
ViewPager.java
public class MyViewPager extends ViewPager implements ViewPager.PageTransformer {
public static final String TAG = "MyViewPager";
private float MAX_SCALE = 0.0f;
private int mPageMargin;
private boolean animationEnabled=true;
private boolean fadeEnabled=false;
private float fadeFactor=0.5f;
public MyViewPager(Context context) {
this(context, null);
}
public MyViewPager(Context context, AttributeSet attrs) {
super(context, attrs);
// clipping should be off on the pager for its children so that they can scale out of bounds.
setClipChildren(false);
setClipToPadding(false);
// to avoid fade effect at the end of the page
setOverScrollMode(2);
setPageTransformer(false, this);
setOffscreenPageLimit(3);
mPageMargin = dp2px(context.getResources(), 50);
setPadding(mPageMargin, mPageMargin, mPageMargin, mPageMargin);
}
public int dp2px(Resources resource, int dp) {
return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, resource.getDisplayMetrics());
}
public void setAnimationEnabled(boolean enable) {
this.animationEnabled = enable;
}
public void setFadeEnabled(boolean fadeEnabled) {
this.fadeEnabled = fadeEnabled;
}
public void setFadeFactor(float fadeFactor) {
this.fadeFactor = fadeFactor;
}
#Override
public void setPageMargin(int marginPixels) {
mPageMargin = marginPixels;
// setPadding(mPageMargin, mPageMargin, mPageMargin, mPageMargin);
}
#Override
public void transformPage(View page, float position) {
if (mPageMargin <= 0|| !animationEnabled)
return;
page.setPadding(mPageMargin / 3, mPageMargin / 3, mPageMargin / 3, mPageMargin / 3);
if (MAX_SCALE == 0.0f && position > 0.0f && position < 1.0f) {
MAX_SCALE = position;
}
position = position - MAX_SCALE;
float absolutePosition = Math.abs(position);
if (position <= -1.0f || position >= 1.0f) {
if(fadeEnabled)
page.setAlpha(fadeFactor);
// Page is not visible -- stop any running animations
} else if (position == 0.0f) {
// Page is selected -- reset any views if necessary
page.setScaleX((1 + MAX_SCALE));
page.setScaleY((1 + MAX_SCALE));
page.setAlpha(1);
} else {
page.setScaleX(1 + MAX_SCALE * (1 - absolutePosition));
page.setScaleY(1 + MAX_SCALE * (1 - absolutePosition));
if(fadeEnabled)
page.setAlpha( Math.max(fadeFactor, 1 - absolutePosition));
}
}
}
UPDATE - if you want to make current page zoom use below PageTransformer
import android.os.Bundle;
import androidx.appcompat.app.AppCompatActivity;
import androidx.viewpager2.widget.ViewPager2;
public class JavaActivity extends AppCompatActivity {
ViewPager2 myViewPager2;
MyAdapter MyAdapter;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_java);
myViewPager2 = findViewById(R.id.viewpager);
MyAdapter = new MyAdapter(this);
myViewPager2.setOrientation(ViewPager2.ORIENTATION_HORIZONTAL);
myViewPager2.setAdapter(MyAdapter);
myViewPager2.setOffscreenPageLimit(3);
float pageMargin = getResources().getDimensionPixelOffset(R.dimen.pageMargin);
float pageOffset = getResources().getDimensionPixelOffset(R.dimen.offset);
myViewPager2.setPageTransformer((page, position) -> {
float myOffset = position * -(2 * pageOffset + pageMargin);
if (position < -1) {
page.setTranslationX(-myOffset);
} else if (position <= 1) {
float scaleFactor = Math.max(0.7f, 1 - Math.abs(position - 0.14285715f));
page.setTranslationX(myOffset);
page.setScaleY(scaleFactor);
page.setAlpha(scaleFactor);
} else {
page.setAlpha(0);
page.setTranslationX(myOffset);
}
});
}
}
OUTPUT
NOTE: you can download complete code from my GitHub repositories
Try this way
JavaActivity
import android.os.Bundle;
import android.view.View;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.view.ViewCompat;
import androidx.viewpager2.widget.ViewPager2;
public class JavaActivity extends AppCompatActivity {
ViewPager2 myViewPager2;
MyAdapter MyAdapter;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_java);
myViewPager2 = findViewById(R.id.viewpager);
MyAdapter = new MyAdapter(this);
myViewPager2.setOrientation(ViewPager2.ORIENTATION_HORIZONTAL);
myViewPager2.setAdapter(MyAdapter);
myViewPager2.setOffscreenPageLimit(3);
float pageMargin= getResources().getDimensionPixelOffset(R.dimen.pageMargin);
float pageOffset = getResources().getDimensionPixelOffset(R.dimen.offset);
myViewPager2.setPageTransformer(new ViewPager2.PageTransformer() {
#Override
public void transformPage(#NonNull View page, float position) {
float myOffset = position * -(2 * pageOffset + pageMargin);
if (myViewPager2.getOrientation() == ViewPager2.ORIENTATION_HORIZONTAL) {
if (ViewCompat.getLayoutDirection(myViewPager2) == ViewCompat.LAYOUT_DIRECTION_RTL) {
page.setTranslationX(-myOffset);
} else {
page.setTranslationX(myOffset);
}
} else {
page.setTranslationY(myOffset);
}
}
});
}
}
activity_java layout file
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".JavaActivity">
<androidx.viewpager2.widget.ViewPager2
android:id="#+id/viewpager"
android:clipToPadding="false"
android:clipChildren="false"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</LinearLayout>
>
MyAdapter
import android.content.Context;
import android.graphics.Color;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
public class MyAdapter extends RecyclerView.Adapter<MyAdapter.MyViewHolder> {
private Context context;
public MyAdapter(Context context) {
this.context = context;
}
#NonNull
#Override
public MyViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(context).inflate(R.layout.row_item, parent, false);
return new MyViewHolder(view);
}
#Override
public void onBindViewHolder(#NonNull MyViewHolder holder, int position) {
holder.tvName.setText(String.format("Row number%d", position));
if (position % 2 ==0){
holder.imgBanner.setBackgroundColor(Color.RED);
}else {
holder.imgBanner.setBackgroundColor(Color.GREEN);
}
}
#Override
public int getItemCount() {
return 15;
}
public class MyViewHolder extends RecyclerView.ViewHolder {
TextView tvName;
ImageView imgBanner;
public MyViewHolder(#NonNull View itemView) {
super(itemView);
tvName = itemView.findViewById(R.id.tvName);
imgBanner = itemView.findViewById(R.id.imgBanner);
}
}
}
row_item layout
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_marginLeft="#dimen/pageMarginAndOffset"
android:layout_marginRight="#dimen/pageMarginAndOffset"
android:layout_height="match_parent"
android:orientation="vertical">
<ImageView
android:id="#+id/imgBanner"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#color/colorAccent"
android:contentDescription="#string/app_name"/>
<TextView
android:id="#+id/tvName"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:gravity="center"
android:layout_centerInParent="true"
android:textColor="#android:color/white"
android:textSize="20sp"
android:textStyle="bold"
tools:text="Hello"/>
</RelativeLayout>
OUTPUT
Using ViewPager2 (that has RecyclerView in it).
vp2 is instance of ViewPager2
This worked for me:
RecyclerView rv = (RecyclerView) vp2.getChildAt(0);
rv.setPadding(80, 0, 80, 0);
rv.setClipToPadding(false);
I used FragmentStateAdapter for it and overriding getItemId() and containsItem() for add/remove fragments.
result is here:
You can make use of clipToPadding and programmatically adding the pageMargin and padding to your viewpager.
Try something like this-
viewpager.setClipToPadding(false);
viewpager.setPadding(40, 0, 70, 0);
viewpager.setPageMargin(20);
I'm creating a Scrolling Activity in my Android App. The Activity has a CollapsingToolbarLayout with parallax effect.
When I scroll the layout below the appbarlayout up, it'll go up smoothly and the appbarlayout will be collapsed up to the title.The ImageView and the TextView will go up to the title. And when I scroll the layout back down, they'll all go back down to the beginning.
The bug is here:
when I running the activity on some devices, sometimes when I scroll it up, the layout will be stucked there up and down for seconds and then, back go to the top.
And when I running the activity on some other devices, it'll be OK, nothing wrong happened.
The demo of this bug: https://share.weiyun.com/1d797a4a92580e1595eacb226f9a92a3
Here is the layout:
<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="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="nczz.cn.helloworld.ScrollingActivity"
>
<android.support.design.widget.AppBarLayout
android:id="#+id/app_bar"
android:layout_width="match_parent"
android:layout_height="#dimen/app_bar_height"
android:background="#FA7199"
app:layout_scrollFlags="scroll|enterAlways"
android:theme="#style/AppTheme.AppBarOverlay">
<nczz.cn.widget.CollapsingImageTextLayout
android:id="#+id/imageTextLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_scrollFlags="scroll|exitUntilCollapsed"
app:title_id="#+id/test_title"
app:text_id="#+id/test_text"
app:img_scale="0.6"
app:text_scale="0.6"
app:text_margin_left="110dp"
app:img_id="#+id/test_img"
app:img_margin_left="55dp"
>
<LinearLayout
android:id="#+id/test_title"
android:layout_width="match_parent"
android:layout_height="80dp"
android:background="#FA7199"
android:gravity="center_vertical"
android:orientation="horizontal"
>
<ImageView
android:id="#+id/return_btn"
android:layout_width="15dp"
android:layout_height="15dp"
android:layout_marginLeft="20dp"
android:layout_centerVertical="true"
android:src="#drawable/left" />
</LinearLayout>
<ImageView
android:id="#+id/test_img"
android:layout_width="80dp"
android:layout_height="80dp"
android:scaleType="fitXY"
android:src="#mipmap/ic_launcher"
android:layout_centerInParent="true"
android:layout_marginBottom="30dp"
/>
<TextView
android:id="#+id/test_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="#+id/test_img"
android:text="MoveText"
android:textSize="20sp"
android:textColor="#android:color/white"
android:layout_marginTop="-20dp"
android:layout_marginLeft="50dp"
android:layout_centerInParent="true"
/>
</nczz.cn.widget.CollapsingImageTextLayout>
</android.support.design.widget.AppBarLayout>
<include
android:id="#+id/includelayout"
layout="#layout/content_scrolling"/>
</android.support.design.widget.CoordinatorLayout>
Here is the CollapsingImageTextLayout:
package nczz.cn.widget;
import android.content.Context;
import android.content.res.TypedArray;
import android.support.annotation.NonNull;
import android.support.design.widget.AppBarLayout;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewParent;
import android.widget.RelativeLayout;
import nczz.cn.helloworld.R;
/**
* Created by yahui.hu on 2017/4/21.
*/
public class CollapsingImageTextLayout extends RelativeLayout {
private AppBarLayout.OnOffsetChangedListener mOffsetChangedListener;
private int mTitleId, mTextId, mImageId;
private int mTitleMarginLeft, mTitleMarginTop, mImgMarginLeft, mImgMarginTop;
private float mTextScale, mImgScale;
private View mTitle, mImg, mText;
private boolean isGetView = true;
private int mTitleHeight = 0;
public CollapsingImageTextLayout(Context context) {
this(context, null);
}
public CollapsingImageTextLayout(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public CollapsingImageTextLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
TypedArray a = context.obtainStyledAttributes(attrs,
R.styleable.CollapsingImageLayout, defStyleAttr, 0);
mTitleId = a.getResourceId(R.styleable.CollapsingImageLayout_title_id, 0);
mTextId = a.getResourceId(R.styleable.CollapsingImageLayout_text_id, 0);
mImageId = a.getResourceId(R.styleable.CollapsingImageLayout_img_id, 0);
mTextScale = a.getFloat(R.styleable.CollapsingImageLayout_text_scale, 0.4f);
mImgScale = a.getFloat(R.styleable.CollapsingImageLayout_img_scale, 0.4f);
mTitleMarginLeft = a.getDimensionPixelSize(R.styleable.CollapsingImageLayout_text_margin_left, 0);
mTitleMarginTop = a.getDimensionPixelSize(R.styleable.CollapsingImageLayout_text_margin_top, 0);
mImgMarginLeft = a.getDimensionPixelSize(R.styleable.CollapsingImageLayout_img_margin_left, 0);
mImgMarginTop = a.getDimensionPixelSize(R.styleable.CollapsingImageLayout_img_margin_top, 0);
a.recycle();
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
getView();
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
private void getView() {
if (!isGetView) {
return;
}
if (mTitleId != 0) {
mTitle = findViewById(mTitleId);
}
if (mTextId != 0) {
mText = findViewById(mTextId);
}
if (mImageId != 0) {
mImg = findViewById(mImageId);
}
isGetView = false;
}
#Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
super.onLayout(changed, l, t, r, b);
if (mTitle != null) {
getViewOffsetHelper(mTitle).onViewLayout(0, 0);
setMinimumHeight(getHeightWithMargins(mTitle));
mTitleHeight = mTitle.getHeight();
this.bringChildToFront(mTitle);
}
if (mImg != null) {
getViewOffsetHelper(mImg).onViewLayout(mImgMarginLeft, mImgMarginTop);
this.bringChildToFront(mImg);
}
if (mText != null) {
getViewOffsetHelper(mText).onViewLayout(mTitleMarginLeft, mTitleMarginTop);
this.bringChildToFront(mText);
}
}
static ViewHelper getViewOffsetHelper(View view) {
ViewHelper offsetHelper = (ViewHelper) view.getTag(R.id.view_helper);
if (offsetHelper == null) {
offsetHelper = new ViewHelper(view);
view.setTag(R.id.view_helper, offsetHelper);
}
return offsetHelper;
}
private static int getHeightWithMargins(#NonNull final View view) {
final ViewGroup.LayoutParams lp = view.getLayoutParams();
if (lp instanceof MarginLayoutParams) {
final MarginLayoutParams mlp = (MarginLayoutParams) lp;
return view.getHeight() + mlp.topMargin + mlp.bottomMargin;
}
return view.getHeight();
}
#Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
ViewParent viewParent = getParent();
if (viewParent instanceof AppBarLayout) {
if (mOffsetChangedListener == null) mOffsetChangedListener = new OffsetListenerImp();
((AppBarLayout) viewParent).addOnOffsetChangedListener(mOffsetChangedListener);
}
}
#Override
protected void onDetachedFromWindow() {
ViewParent viewParent = getParent();
if (viewParent instanceof AppBarLayout) {
((AppBarLayout) viewParent).removeOnOffsetChangedListener(mOffsetChangedListener);
}
super.onDetachedFromWindow();
}
final int getMaxOffsetForPinChild(View child) {
final ViewHelper offsetHelper = getViewOffsetHelper(child);
final LayoutParams lp = (LayoutParams) child.getLayoutParams();
return getHeight()
- offsetHelper.getLayoutTop()
- child.getHeight()
- lp.bottomMargin;
}
static int constrain(int amount, int low, int high) {
return amount < low ? low : (amount > high ? high : amount);
}
static int constrain(int amount, int low) {
return amount < low ? low : amount;
}
private void setTopAndBottomOffset(View child, int verticalOffset) {
ViewHelper viewHelper = (ViewHelper) child.getTag(R.id.view_helper);
viewHelper.setTopAndBottomOffset(
constrain(-verticalOffset, 0, getMaxOffsetForPinChild(child)));
Log.e("setTopAndBottomOffset",""+-verticalOffset);
}
private void setTopAndBottomOffset(View child, int verticalOffset, float scale) {
ViewHelper viewHelper = (ViewHelper) child.getTag(R.id.view_helper);
viewHelper.setTopAndBottomOffset(
constrain(-verticalOffset - getMaxOffset(viewHelper, scale),
0));
//Log.e("setTopAndBottomOffset",""+-verticalOffset);
}
private void setLeftAndRightOffset(View child, int verticalOffset, float scale) {
ViewHelper viewHelper = (ViewHelper) child.getTag(R.id.view_helper);
int maxOffsetDistance = getMaxOffset(viewHelper, scale);
int maxLeft = viewHelper.getLayoutLeft()
+ (viewHelper.getViewWidth() - viewHelper.getScaleViewWidth(scale))
- viewHelper.getMarginTitleLeft();
int realOffset = (int) (maxLeft * 1.0f / (maxOffsetDistance * 1.0f) * verticalOffset);
realOffset = constrain(realOffset, -maxLeft, maxLeft);
viewHelper.setLeftAndRightOffset(realOffset);
// Log.e("setLeftAndRightOffset",""+realOffset);
}
private void setViewScale(View child, int verticalOffset, float scale) {
ViewHelper viewHelper = (ViewHelper) child.getTag(R.id.view_helper);
int maxOffsetDistance = getMaxOffset(viewHelper, scale);
float realScale = -verticalOffset - maxOffsetDistance > 0 ? scale : verticalOffset == 0 ? 1f : 0f;
if (realScale == 0) {
realScale = (maxOffsetDistance + verticalOffset * (1 - scale)) / (maxOffsetDistance * 1f);
}
viewHelper.setViewOffsetScale(realScale);
}
private int getMaxOffset(ViewHelper viewHelper, float scale) {
int scaleViewHeight = (int) (scale * viewHelper.getViewHeight());
int offsetTitleDistance = scaleViewHeight >= mTitleHeight ? 0 : (mTitleHeight - scaleViewHeight) / 2;
int marginTop = viewHelper.getMarginTitleTop() >= offsetTitleDistance ? offsetTitleDistance : viewHelper.getMarginTitleTop();
return viewHelper.getLayoutBottom() - viewHelper.getScaleViewHeight(scale) - offsetTitleDistance - marginTop;
}
private class OffsetListenerImp implements AppBarLayout.OnOffsetChangedListener {
#Override
public void onOffsetChanged(AppBarLayout appBarLayout, int verticalOffset) {
if (mTitle != null) {
setTopAndBottomOffset(mTitle, verticalOffset);
}
if (mText != null) {
setTopAndBottomOffset(mText, verticalOffset, mTextScale);
setLeftAndRightOffset(mText, verticalOffset, mTextScale);
setViewScale(mText, verticalOffset, mTextScale);
}
if (mImg != null) {
setTopAndBottomOffset(mImg, verticalOffset, mImgScale);
setLeftAndRightOffset(mImg, verticalOffset, mImgScale);
setViewScale(mImg, verticalOffset, mImgScale);
}
}
}
public void setImgTitleMarginTop(int top) {
if (mImg != null) {
getViewOffsetHelper(mImg).setMarginTitleTop(top);
}
}
public void setImgTitleMarginLeft(int left) {
if (mImg != null) {
getViewOffsetHelper(mImg).setMarginTitleLeft(left);
}
}
public void setTextTitleMarginTop(int top) {
if (mText != null) {
getViewOffsetHelper(mText).setMarginTitleTop(top);
}
}
public void setImgTextMarginLeft(int left) {
if (mText != null) {
getViewOffsetHelper(mText).setMarginTitleLeft(left);
}
}
}
Here is the content_scolling.xml:
<?xml version="1.0" encoding="utf-8"?>
<android.support.v4.widget.NestedScrollView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:background="#cccccc"
android:layout_height="match_parent"
app:layout_behavior="#string/appbar_scrolling_view_behavior"
tools:context="nczz.cn.helloworld.ScrollingActivity"
tools:showIn="#layout/activity_scrolling">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#string/large_text" />
</android.support.v4.widget.NestedScrollView>
Here is the java:
package nczz.cn.helloworld;
import android.app.Activity;
import android.content.Intent;
import android.view.View;
import android.view.ViewGroup;
import android.view.Window;
import android.view.WindowManager;
import android.widget.LinearLayout;
public class ScrollingActivity extends Activity {
LinearLayout titleTxt;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
this.requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.activity_scrolling);
initViews();
setTitleBarHeight();
}
private void initViews(){
titleTxt= (LinearLayout) findViewById(R.id.test_title);
}
private void setTitleBarHeight(){
WindowManager manager=getWindowManager();
int height=manager.getDefaultDisplay().getHeight();
ViewGroup.LayoutParams params=titleTxt.getLayoutParams();
params.height=height/12;
titleTxt.setLayoutParams(params);
}
}
i am not sure but u can use following code according to view inside nested scrollview
viewlayoutInsidescrollview.setNestedScrollingEnabled(false);
in java class
I have a custom image of a RatingBar and I wanted to auto-adjust the image height depending on the space left from the parent layouts.
Here's the layout:
<ImageView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#drawable/rateus_bg"
android:scaleType="fitXY" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<com.ims.westmont.android.view.TextViewUniSans
android:id="#+id/textview_rate_us"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1"
android:gravity="center"
android:text="#string/rate_us"
android:textAllCaps="true"
android:textColor="#android:color/white"
android:textSize="60sp"
android:textStyle="bold" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1"
android:gravity="center"
android:orientation="vertical">
<RatingBar
android:id="#+id/ratingbar_rate"
style="#style/rateUsBar"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:numStars="5"
android:rating="0"
android:stepSize="0.5" />
</LinearLayout>
<com.ims.westmont.android.view.TextViewUniSans
android:id="#+id/button_send"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:text="#string/send"
android:textColor="#android:color/white"
android:textSize="60sp"
android:textAllCaps="true"
android:layout_weight="1"
android:gravity="center"/>
</LinearLayout>
Here's the visual representation:
Since I am using a LinearLayout, the three elements in it have equal height. The RatingBar uses a custom style:
<style name="rateUsBar" parent="#android:style/Widget.RatingBar">
<item name="android:progressDrawable">#drawable/rating_stars</item>
<item name="android:minHeight">90dp</item>
<item name="android:maxHeight">90dp</item>
</style>
And finally the drawable rating_stars:
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="#android:id/background"
android:drawable="#drawable/rate_empty" />
<item
android:id="#android:id/secondaryProgress"
android:drawable="#drawable/rate_empty" />
<item
android:id="#android:id/progress"
android:drawable="#drawable/rate_filled" />
</layer-list>
Is this a weight issue or should I create a custom view (class) to adjust height?
One solution is
Use Different Values folder
values
2.values-mdpi, values-mdpi-land 3 values-sw320dp, values-sw320dp-land 4 values-sw480dp, values-sw480dp-land 5 values-sw600dp, values-sw600dp-land 6 values-sw720dp, values-sw720dp-land
Now put your style.xml file on each of this folder and according to your device set the height and width respectively.
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:orientation="vertical">
<RatingBar
android:id="#+id/ratingbar_rate"
style="#style/rateUsBar"
android:numStars="5"
android:rating="0"
android:stepSize="0.5" />
</LinearLayout>
You can check preview your layout for different device in preview option.
if you get any problem using below code let me know.
By following below code you will be able to set the height and width with any value :)
put it inside customratingbar.java
package com.example.ashis.myapplication;
import android.annotation.TargetApi;
import android.content.Context;
import android.content.res.TypedArray;
import android.os.Build;
import android.support.annotation.NonNull;
import android.support.v4.view.ViewCompat;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.LinearLayout;
import com.example.ashis.myapplication.R;
/**
* Created by poliveira on 07/08/2014.
*/
/**
* regular rating bar. it wraps the stars making its size fit the parent
*/
public class customratingbar extends LinearLayout {
public IRatingBarCallbacks getOnScoreChanged() {
return onScoreChanged;
}
public void setOnScoreChanged(IRatingBarCallbacks onScoreChanged) {
this.onScoreChanged = onScoreChanged;
}
public interface IRatingBarCallbacks {
void scoreChanged(float score);
}
private int mMaxStars = 5;
private float mCurrentScore = 2.5f;
private int mStarOnResource = R.mipmap.stars;
private int mStarOffResource = R.mipmap.ic_launcheroff;
private int mStarHalfResource = R.mipmap.stars;
private ImageView[] mStarsViews;
private float mStarPadding;
private IRatingBarCallbacks onScoreChanged;
private int mLastStarId;
private boolean mOnlyForDisplay;
private double mLastX;
private boolean mHalfStars = true;
public customratingbar(Context context) {
super(context);
init();
}
public float getScore() {
return mCurrentScore;
}
public void setScore(float score) {
score = Math.round(score * 2) / 2.0f;
if (!mHalfStars)
score = Math.round(score);
mCurrentScore = score;
refreshStars();
}
public void setScrollToSelect(boolean enabled) {
mOnlyForDisplay = !enabled;
}
public customratingbar(Context context, AttributeSet attrs) {
super(context, attrs);
initializeAttributes(attrs, context);
init();
}
private void initializeAttributes(AttributeSet attrs, Context context) {
TypedArray a = context.obtainStyledAttributes(attrs,
R.styleable.CustomRatingBar);
final int N = a.getIndexCount();
for (int i = 0; i < N; ++i) {
int attr = a.getIndex(i);
if (attr == R.styleable.CustomRatingBar_maxStars)
mMaxStars = a.getInt(attr, 5);
else if (attr == R.styleable.CustomRatingBar_stars)
mCurrentScore = a.getFloat(attr, 2.5f);
else if (attr == R.styleable.CustomRatingBar_starHalf)
mStarHalfResource = a.getResourceId(attr, android.R.drawable.star_on);
else if (attr == R.styleable.CustomRatingBar_starOn)
mStarOnResource = a.getResourceId(attr, android.R.drawable.star_on);
else if (attr == R.styleable.CustomRatingBar_starOff)
mStarOffResource = a.getResourceId(attr, android.R.drawable.star_off);
else if (attr == R.styleable.CustomRatingBar_starPadding)
mStarPadding = a.getDimension(attr, 0);
else if (attr == R.styleable.CustomRatingBar_onlyForDisplay)
mOnlyForDisplay = a.getBoolean(attr, false);
else if (attr == R.styleable.CustomRatingBar_halfStars)
mHalfStars = a.getBoolean(attr, true);
}
a.recycle();
}
#TargetApi(Build.VERSION_CODES.HONEYCOMB)
public customratingbar(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
initializeAttributes(attrs, context);
init();
}
void init() {
mStarsViews = new ImageView[mMaxStars];
for (int i = 0; i < mMaxStars; i++) {
ImageView v = createStar();
addView(v);
mStarsViews[i] = v;
}
refreshStars();
}
#Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
return true;
}
/**
* hardcore math over here
*
* #param x
* #return
*/
private float getScoreForPosition(float x) {
if (mHalfStars)
return (float) Math.round(((x / ((float) getWidth() / (mMaxStars * 3f))) / 3f) * 2f) / 2;
float value = (float) Math.round((x / ((float) getWidth() / (mMaxStars))));
return value < 0 ? 1 : value;
}
private int getImageForScore(float score) {
if (score > 0)
return Math.round(score) - 1;
else return -1;
}
private void refreshStars() {
boolean flagHalf = (mCurrentScore != 0 && (mCurrentScore % 0.5 == 0)) && mHalfStars;
for (int i = 1; i <= mMaxStars; i++) {
if (i <= mCurrentScore)
mStarsViews[i - 1].setImageResource(mStarOnResource);
else {
if (flagHalf && i - 0.5 <= mCurrentScore)
mStarsViews[i - 1].setImageResource(mStarHalfResource);
else
mStarsViews[i - 1].setImageResource(mStarOffResource);
}
}
}
private ImageView createStar() {
ImageView v = new ImageView(getContext());
LayoutParams params = new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
params.weight = 1;
v.setPadding((int) mStarPadding, 0, (int) mStarPadding, 0);
v.setAdjustViewBounds(true);
v.setScaleType(ImageView.ScaleType.FIT_CENTER);
v.setLayoutParams(params);
v.setImageResource(mStarOffResource);
return v;
}
private ImageView getImageView(int position) {
try {
return mStarsViews[position];
} catch (Exception e) {
return null;
}
}
#Override
public boolean onTouchEvent(#NonNull MotionEvent event) {
if (mOnlyForDisplay)
return true;
switch (event.getAction()) {
case MotionEvent.ACTION_UP:
animateStarRelease(getImageView(mLastStarId));
mLastStarId = -1;
break;
case MotionEvent.ACTION_MOVE:
if (Math.abs(event.getX() - mLastX) > 50)
requestDisallowInterceptTouchEvent(true);
float lastscore = mCurrentScore;
mCurrentScore = getScoreForPosition(event.getX());
if (lastscore != mCurrentScore) {
animateStarRelease(getImageView(mLastStarId));
animateStarPressed(getImageView(getImageForScore(mCurrentScore)));
mLastStarId = getImageForScore(mCurrentScore);
refreshStars();
if (onScoreChanged != null)
onScoreChanged.scoreChanged(mCurrentScore);
}
break;
case MotionEvent.ACTION_DOWN:
mLastX = event.getX();
lastscore = mCurrentScore;
mCurrentScore = getScoreForPosition(event.getX());
animateStarPressed(getImageView(getImageForScore(mCurrentScore)));
mLastStarId = getImageForScore(mCurrentScore);
if (lastscore != mCurrentScore) {
refreshStars();
if (onScoreChanged != null)
onScoreChanged.scoreChanged(mCurrentScore);
}
}
return true;
}
private void animateStarPressed(ImageView star) {
if (star != null)
ViewCompat.animate(star).scaleX(1.2f).scaleY(1.2f).setDuration(100).start();
}
private void animateStarRelease(ImageView star) {
if (star != null)
ViewCompat.animate(star).scaleX(1f).scaleY(1f).setDuration(100).start();
}
public boolean isHalfStars() {
return mHalfStars;
}
public void setHalfStars(boolean halfStars) {
mHalfStars = halfStars;
}
}
and put it inside mainactivity.java
package com.example.ashis.myapplication;
import android.os.Bundle;
import android.support.design.widget.FloatingActionButton;
import android.support.design.widget.Snackbar;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.view.View;
import android.view.Menu;
import android.view.MenuItem;
public class MainActivity extends AppCompatActivity
{
customratingbar customratingbar;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
customratingbar= (com.example.ashis.myapplication.customratingbar) findViewById(R.id.rating);
customratingbar.setOnScoreChanged(new customratingbar.IRatingBarCallbacks() {
#Override
public void scoreChanged(float score) {
customratingbar.setScore(score);
}
});
FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
fab.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG)
.setAction("Action", null).show();
}
});
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.menu_main, menu);
return true;
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();
//noinspection SimplifiableIfStatement
if (id == R.id.action_settings) {
return true;
}
return super.onOptionsItemSelected(item);
}
}
and put it in res/values/attrs.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="CustomRatingBar">
<attr name="maxStars" format="integer"/>
<attr name="stars" format="float"/>
<attr name="starOff" format="reference|integer"/>
<attr name="starOn" format="reference|integer"/>
<attr name="starHalf" format="reference|integer"/>
<attr name="starPadding" format="dimension"/>
<attr name="onlyForDisplay" format="boolean"/>
<attr name="halfStars" format="boolean"/>
</declare-styleable>
</resources>
and inside res/layout/content_main.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
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"
app:layout_behavior="#string/appbar_scrolling_view_behavior"
tools:showIn="#layout/activity_main"
tools:context="com.example.ashis.myapplication.MainActivity">
<com.example.ashis.myapplication.customratingbar
android:layout_width="300dp"
android:id="#+id/rating"
android:layout_height="20dp"
>
</com.example.ashis.myapplication.customratingbar>
<TextView
android:text="Hello World!"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</RelativeLayout>
I have created custom view which extends FrameLayout. After adding it into RelativeLayout it's being displayed as two nested views:
Is it normal? It sometimes messes up with wrap_content flags but I couldn't figure out why. When I use View as a base class everything looks normal.
Here is my code:
MainActivity.java
package com.example.app;
import android.support.v7.app.ActionBarActivity;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.SeekBar;
import com.codersmill.tset.R;
public class MainActivity extends ActionBarActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
}
activity_main.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<com.example.app.RateBar
android:layout_width="match_parent"
android:layout_height="48dp"
android:id="#+id/rateBar"
android:layout_alignParentTop="true"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"/>
</RelativeLayout>
RateBar.java
package com.example.app;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
import android.content.Context;
import android.util.AttributeSet;
import android.view.View;
import android.widget.FrameLayout;
import android.widget.TextView;
import com.codersmill.tset.R;
public class RateBar extends FrameLayout {
TextView star1, star2, star3, star4, star5;
View dot;
private boolean isAnimating = false;
int radius = 36;
private int step = 100;
private int leftMargin = 50;
private int topMargin = 0;
private int currentPosition = 0;
public RateBar(Context context) {
this(context, null);
}
public RateBar(Context context, AttributeSet attrs) {
super(context, attrs);
View.inflate(context, R.layout.view_rate_bar, this);
star1 = (TextView) this.findViewById(R.id.star1);
star2 = (TextView) this.findViewById(R.id.star2);
star3 = (TextView) this.findViewById(R.id.star3);
star4 = (TextView) this.findViewById(R.id.star4);
star5 = (TextView) this.findViewById(R.id.star5);
star1.setOnClickListener(onClickListener);
star2.setOnClickListener(onClickListener);
star3.setOnClickListener(onClickListener);
star4.setOnClickListener(onClickListener);
star5.setOnClickListener(onClickListener);
dot = this.findViewById(R.id.selector);
}
public RateBar(Context context, AttributeSet attrs, int defStyleAttr) {
this(context, attrs);
}
private OnClickListener onClickListener = new OnClickListener() {
#Override public void onClick(View v) {
if(isAnimating) return;
switch (v.getId()) {
case R.id.star1:
animateToPosition(0);
break;
case R.id.star2:
animateToPosition(1);
break;
case R.id.star3:
animateToPosition(2);
break;
case R.id.star4:
animateToPosition(3);
break;
case R.id.star5:
animateToPosition(4);
break;
}
}
};
#Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
step = (int) (getMeasuredWidth() / 5.0f);
leftMargin = (int) (step / 2.0f - radius);
topMargin = (int) (getMeasuredHeight() / 2.0f - radius);
if(!isAnimating) {
FrameLayout.LayoutParams params = (LayoutParams) dot.getLayoutParams();
params.leftMargin = leftMargin + currentPosition * step;
params.topMargin = topMargin;
dot.setLayoutParams(params);
}
}
private void animateToPosition(final int position) {
final int from = currentPosition*step + leftMargin;
final int to = position*step + leftMargin;
ValueAnimator animation = ValueAnimator.ofInt(from, to);
animation.setDuration(250);
animation.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
#Override public void onAnimationUpdate(ValueAnimator animation) {
FrameLayout.LayoutParams params = (LayoutParams) dot.getLayoutParams();
params.leftMargin = (int) animation.getAnimatedValue();
params.topMargin = topMargin;
dot.setLayoutParams(params);
}
});
animation.addListener(new AnimatorListenerAdapter() {
#Override public void onAnimationStart(Animator animation) {
super.onAnimationStart(animation);
isAnimating = true;
FrameLayout.LayoutParams params = (LayoutParams) dot.getLayoutParams();
params.topMargin = topMargin;
params.leftMargin = currentPosition * step + leftMargin;
dot.setLayoutParams(params);
}
#Override public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation);
isAnimating = false;
currentPosition = position;
FrameLayout.LayoutParams params = (LayoutParams) dot.getLayoutParams();
params.leftMargin = currentPosition * step + leftMargin;
params.topMargin = topMargin;
dot.setLayoutParams(params);
}
});
animation.start();
}
private void updateDotPosition() {
}
}
view_rate_bar.xml
<?xml version="1.0" encoding="utf-8"?>
<merge xmlns:android="http://schemas.android.com/apk/res/android" android:layout_height="wrap_content">
<View
android:id="#+id/selector"
android:layout_width="24dp"
android:layout_height="24dp"
android:background="#drawable/oval"
/>
<LinearLayout
android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="match_parent"
android:layout_alignParentTop="true"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true">
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:text="1"
android:id="#+id/star1"
android:layout_weight="1"
android:gravity="center"
android:padding="12dp"
android:background="#2200ff00"/>
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:text="2"
android:id="#+id/star2"
android:layout_weight="1"
android:gravity="center"
android:padding="12dp"
android:background="#22ff0000"/>
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:text="3"
android:id="#+id/star3"
android:layout_weight="1"
android:gravity="center"
android:padding="12dp"
android:background="#2200ff00"/>
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:text="4"
android:id="#+id/star4"
android:layout_weight="1"
android:gravity="center"
android:padding="12dp"
android:background="#22ff0000"/>
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:text="5"
android:id="#+id/star5"
android:layout_weight="1"
android:gravity="center"
android:padding="12dp"
android:background="#2200ff00" />
</LinearLayout>
</merge>
even i was facing the same issue, it appears that new version of android studio comes up with two files content_main.xml and activity_mail.xml , when we select Activity_main.xml>Design view everything appears 'Custom View' , instead when we highlight content_main.xml>Design, everything is bac to normal. I dont know why it happens but that;s how i fix mine ( android nooob here )
More can be found here : https://teamtreehouse.com/community/i-cant-drag-widgets-onto-the-phone-mockup-component-tree-shows-customview-instead-of-the-relative-view-help