I'm running a rotation animation on a ImageView. The thing is the bitmap has been cut to fit exactly the ImageView. So when it rotate, it doesn't cover completely the ImageView as well as the screen.
This is what I want
And this is what happened
xml
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout 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=".fragments.LevelCompletedFragment">
<RelativeLayout
android:id="#+id/vgBackground"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#3f5d68"
android:alpha="1">
</RelativeLayout>
<ImageView
android:id="#+id/ivStarburst"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:adjustViewBounds="true"
android:scaleType="centerCrop"
android:src="#drawable/star" />
</FrameLayout>
Run animation
Animation shake = AnimationUtils.loadAnimation(mContext, R.anim.clockwise_rotation);
**ivStarburst.startAnimation(shake);
Edit: After following the solution by #deadfish the animation run correctly only when I open the fragment in onCreate() in MainActivity. If I trigger the fragment in onClick() method, it doesn't work.
MainActivity.java
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
btn.setOnClickListener(this);
}
#Override
public void onClick(View v) {
MainFragment frgm = new MainFragment ();
getSupportFragmentManager().beginTransaction()
.replace(R.id.fragment_container, frgm).commit();
}
You almost did it but forget two things:
setting ImageView width and height as match_layout will match only to the parent's border. You must set custom dimenstion so when You center image, it will fill the whole view.
during image's rotation when the image reaches degree 45 we can notice the image doesn't fill the whole view. Why? Try put two rectangles on themself and then rotate one of them by 45 degree. Here is the result. According to definition of square, it's diagonal value is bigger than one of the sides of a square. So we must cover this.
Here is the solution.
we set custom dimension for ImageView basing on the longest side of the screen
we rewrite width to use diagonal width of square instead of single side
Most of the code is the same as Yours. I used fragment to show the sample.
//MainActivity part
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main_activity);
this.<Button>findViewById(R.id.btn).setOnClickListener(this);
}
#Override
public void onClick(View v) {
getSupportFragmentManager()
.beginTransaction()
.replace(R.id.container, MainFragment.newInstance())
.commit();
}
}
// Fragment part
public class MainFragment extends Fragment {
public static MainFragment newInstance() {
return new MainFragment();
}
#Nullable
#Override
public View onCreateView(#NonNull LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
return inflater.inflate(R.layout.main_fragment, container, false);
}
#Override
public void onViewCreated(#NonNull View view, #Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
ImageView imageView = Objects.requireNonNull(view).findViewById(R.id.imageView);
// Manually set image for ImageView
setImageForView(imageView, R.drawable.star);
// Turn ImageView to square where a = Max(Screen.width, Screen.height)
changeDimensionToSquareForView(imageView);
// Start rotations
animateRotation(imageView);
}
private void changeDimensionToSquareForView(ImageView imageView) {
DisplayMetrics displayMetrics = Resources.getSystem().getDisplayMetrics();
double maxWidth = Math.max(displayMetrics.widthPixels, displayMetrics.heightPixels);
//calc diagonal line, x = a * sqrt(2)
maxWidth = (maxWidth * (Math.sqrt(2)));
imageView.setLayoutParams(new FrameLayout.LayoutParams((int) maxWidth, (int) maxWidth, Gravity.CENTER));
}
private void setImageForView(ImageView imageView, #DrawableRes int imageRes) {
Context context = imageView.getContext();
imageView.setImageDrawable(context.getResources().getDrawable(imageRes, context.getTheme()));
}
// Call this method in onClick method
public void animateRotation(ImageView imageView) {
Context context = imageView.getContext();
Animation shake = AnimationUtils.loadAnimation(context, R.anim.clockwise_rotation);
imageView.startAnimation(shake);
}
}
Here is the layout of fragment:
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/main"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".ui.main.MainFragment">
<ImageView
android:id="#+id/imageView"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:src="#drawable/star" />
</FrameLayout>
Here is the animation xml:
<?xml version="1.0" encoding="utf-8"?>
<rotate xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="15000"
android:fromDegrees="0"
android:interpolator="#android:anim/linear_interpolator"
android:pivotX="50%"
android:pivotY="50%"
android:startOffset="0"
android:toDegrees="360" />
Here is the result:
Related
I am using this solution to round corners of dialog in BottomSheetDialogFragment and it works fine with API 21 and higher
But in Api < 21 it removes the background and the rounded background goes away.
How to make the background rounded in API < 21?
If it is not possible to change the background, please help me change the background color instead.
Morteza I made the code which makes the BottomSheetDialog Fragment dialog round corner by the following code and I tested it in KitKat version mobile as well.
Bottom Sheet Dialog Class code
public class MyBottomSheetDialog extends BottomSheetDialogFragment {
String string;
static MyBottomSheetDialog newInstance(String string) {
MyBottomSheetDialog f = new MyBottomSheetDialog();
Bundle args = new Bundle();
args.putString("string", string);
f.setArguments(args);
return f;
}
#Override
public void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
string = getArguments().getString("string");
//bottom sheet round corners can be obtained but the while background appears to remove that we need to add this.
setStyle(DialogFragment.STYLE_NO_FRAME,0);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.bottom_sheet_modal, container, false);
TextView tv = (TextView) v.findViewById(R.id.text);
//dialog cancel when touches outside (Optional)
getDialog().setCanceledOnTouchOutside(true);
return v;
}}
bottom_sheet_modal.xml
<?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:id="#+id/linearLayout"
android:orientation="vertical"
android:paddingBottom="10dp"
android:paddingLeft="10dp"
android:paddingRight="10dp"
android:paddingTop="10dp"
//adding background from drawable
android:background="#drawable/rounded_dialog">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:layout_gravity="center"
android:gravity="center"
android:weightSum="10"
android:layout_marginStart="20dp"
android:layout_marginEnd="20dp">
<Button
android:layout_width="0dp"
android:layout_weight="5"
android:layout_height="wrap_content"
android:text="Buy"
/>
<Button
android:layout_width="0dp"
android:layout_weight="5"
android:layout_height="wrap_content"
android:text="sell"
/>
</LinearLayout>
</LinearLayout>
rounded_dialog.xml
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="#444343"/>
<corners android:topLeftRadius="16dp"
android:topRightRadius="16dp"/>
</shape>
MainActivity.java
public class MainActivity extends AppCompatActivity {
BottomSheetDialogFragment bottomSheetDialogFragment;
Button button;
LinearLayout linearLayout;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
bottomSheetDialogFragment = MyBottomSheetDialog.newInstance("Bottom Sheet Dialog");
button = findViewById(R.id.button);
button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
bottomSheetDialogFragment.show(getSupportFragmentManager(),bottomSheetDialogFragment.getTag());
}
});
}
}
Try this and let me know #Morteza. Happy coding.
Create a custom drawable rounded_dialog.xml:
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="#android:color/white"/>
<corners android:topLeftRadius="16dp"
android:topRightRadius="16dp"/>
</shape>
view!!.getViewTreeObserver().addOnGlobalLayoutListener(object : ViewTreeObserver.OnGlobalLayoutListener {
override fun onGlobalLayout() {
if (Build.VERSION.SDK_INT < 16) {
view!!.getViewTreeObserver().removeGlobalOnLayoutListener(this)
} else {
view!!.getViewTreeObserver().removeOnGlobalLayoutListener(this)
}
val dialog = dialog as BottomSheetDialog?
val bottomSheet =
dialog!!.findViewById<View>(com.google.android.material.R.id.design_bottom_sheet) as FrameLayout?
//Change background Image for all android versions below Api < 21
bottomSheet!!.setBackgroundResource(R.drawable.rounded_dialog)
}
})
The simplest and cleanest solution, that worked for me, was to put the following 3 lines in onViewCreated(View view, Bundle savedInstanceState) method of my fragment class:
#Override
public void onViewCreated(#NonNull View view, #Nullable Bundle savedInstanceState) {
View bottomSheet = (View) view.getParent();
bottomSheet.setBackgroundTintMode(PorterDuff.Mode.CLEAR);
bottomSheet.setBackgroundTintList(ColorStateList.valueOf(Color.TRANSPARENT));
bottomSheet.setBackgroundColor(Color.TRANSPARENT);
}
This will allow for your custom drawable with rounded corners to be properly shown once set as the background of the top level view of your fragment layout.
In essence this overrides the default BottomSheetFragment attributes regarding color, tintMode and tintList.
Using this, there is no need for messing with style resources.
Please help, I'm trying to implement the same transitions.
1)https://storage.googleapis.com/spec-host-backup/mio-design%2Fassets%2F1tAlSW8Kp7JlXJNo16cv6RZqUl1iNsjen%2Fcards-transition.mp4
2)https://storage.googleapis.com/spec-host-backup/mio-design%2Fassets%2F1qIHOMquJE7flVh1ttDTSogXdvEX2lY_1%2F01-list-parentchild.mp4
But I don`t know how can I do it.
You need put this parameter into imageview of the first activity (xml):
android:transitionName="your_transaction_name"
And when you want open the other activity:
ImageView imageView = findViewById(R.id.your_image_id);
Pair pair = new Pair<>(imageView, ViewCompat.getTransitionName(imageView));
ActivityOptionsCompat transitionActivityOptions = ActivityOptionsCompat.makeSceneTransitionAnimation(this, pair);
Intent intent = new Intent(this, YourOtherActivity.class);
ActivityCompat.startActivityForResult(this, intent, 0, transitionActivityOptions.toBundle());
Add on your OtherActivity the same parameter to your imageView:
android:transitionName="your_transaction_name"
Note: It works only with android API > 21
The transition in the gif on the left side transitions the list element into the content area of the second activity (Toolbar stays in place). In the gif on the right side, the transition transforms the list element into the complete screen of the second activity. The following code provides the effect in the left gif. However, it should be possible to adapt the solution with minor modifications to achieve the transition in the right gif.
Note this only works on Lollipop. However, it is possible to mock a different effect on older devices. Furthermore, the sole purpose of the provided code is to show how it could be done. Don't use this directly in your app.
MainActivity:
public class MainActivity extends AppCompatActivity {
MyAdapter myAdapter;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
setSupportActionBar((Toolbar) findViewById(R.id.toolbar));
ListView listView = (ListView) findViewById(R.id.list_view);
myAdapter = new MyAdapter(this, 0, DataSet.get());
listView.setAdapter(myAdapter);
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, final View view, final int position, long id) {
startTransition(view, myAdapter.getItem(position));
}
});
}
private void startTransition(View view, Element element) {
Intent i = new Intent(MainActivity.this, DetailActivity.class);
i.putExtra("ITEM_ID", element.getId());
Pair<View, String>[] transitionPairs = new Pair[4];
transitionPairs[0] = Pair.create(findViewById(R.id.toolbar), "toolbar"); // Transition the Toolbar
transitionPairs[1] = Pair.create(view, "content_area"); // Transition the content_area (This will be the content area on the detail screen)
// We also want to transition the status and navigation bar barckground. Otherwise they will flicker
transitionPairs[2] = Pair.create(findViewById(android.R.id.statusBarBackground), Window.STATUS_BAR_BACKGROUND_TRANSITION_NAME);
transitionPairs[3] = Pair.create(findViewById(android.R.id.navigationBarBackground), Window.NAVIGATION_BAR_BACKGROUND_TRANSITION_NAME);
Bundle b = ActivityOptionsCompat.makeSceneTransitionAnimation(MainActivity.this, transitionPairs).toBundle();
ActivityCompat.startActivity(MainActivity.this, i, b);
}
}
activity_main.xml:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<android.support.v7.widget.Toolbar
android:id="#+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="#color/colorPrimary"
android:transitionName="toolbar" />
<ListView
android:id="#+id/list_view"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>
DetailActivity:
public class DetailActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_detail);
setSupportActionBar((Toolbar) findViewById(R.id.toolbar));
long elementId = getIntent().getLongExtra("ITEM_ID", -1);
Element element = DataSet.find(elementId);
((TextView) findViewById(R.id.title)).setText(element.getTitle());
((TextView) findViewById(R.id.description)).setText(element.getDescription());
// if we transition the status and navigation bar we have to wait till everything is available
TransitionHelper.fixSharedElementTransitionForStatusAndNavigationBar(this);
// set a custom shared element enter transition
TransitionHelper.setSharedElementEnterTransition(this, R.transition.detail_activity_shared_element_enter_transition);
}
}
activity_detail.xml:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<android.support.v7.widget.Toolbar
android:id="#+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="#color/colorPrimary"
android:transitionName="toolbar" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#abc"
android:orientation="vertical"
android:paddingBottom="200dp"
android:transitionName="content_area"
android:elevation="10dp">
<TextView
android:id="#+id/title"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<TextView
android:id="#+id/description"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>
</LinearLayout>
detail_activity_shared_element_enter_transition.xml (/res/transition/):
<?xml version="1.0" encoding="utf-8"?>
<transitionSet xmlns:android="http://schemas.android.com/apk/res/android"
android:transitionOrdering="together">
<changeBounds/>
<changeTransform/>
<changeClipBounds/>
<changeImageTransform/>
<transition class="my.application.transitions.ElevationTransition"/>
</transitionSet>
my.application.transitions.ElevationTransition:
#TargetApi(Build.VERSION_CODES.LOLLIPOP)
public class ElevationTransition extends Transition {
private static final String PROPNAME_ELEVATION = "my.elevation:transition:elevation";
public ElevationTransition() {
}
public ElevationTransition(Context context, AttributeSet attrs) {
super(context, attrs);
}
#Override
public void captureStartValues(TransitionValues transitionValues) {
captureValues(transitionValues);
}
#Override
public void captureEndValues(TransitionValues transitionValues) {
captureValues(transitionValues);
}
private void captureValues(TransitionValues transitionValues) {
Float elevation = transitionValues.view.getElevation();
transitionValues.values.put(PROPNAME_ELEVATION, elevation);
}
#Override
public Animator createAnimator(ViewGroup sceneRoot, TransitionValues startValues, TransitionValues endValues) {
if (startValues == null || endValues == null) {
return null;
}
Float startVal = (Float) startValues.values.get(PROPNAME_ELEVATION);
Float endVal = (Float) endValues.values.get(PROPNAME_ELEVATION);
if (startVal == null || endVal == null || startVal.floatValue() == endVal.floatValue()) {
return null;
}
final View view = endValues.view;
ValueAnimator a = ValueAnimator.ofFloat(startVal, endVal);
a.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
#Override
public void onAnimationUpdate(ValueAnimator animation) {
view.setElevation((float)animation.getAnimatedValue());
}
});
return a;
}
}
TransitionHelper:
public class TransitionHelper {
public static void fixSharedElementTransitionForStatusAndNavigationBar(final Activity activity) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP)
return;
final View decor = activity.getWindow().getDecorView();
if (decor == null)
return;
activity.postponeEnterTransition();
decor.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
#TargetApi(Build.VERSION_CODES.LOLLIPOP)
#Override
public boolean onPreDraw() {
decor.getViewTreeObserver().removeOnPreDrawListener(this);
activity.startPostponedEnterTransition();
return true;
}
});
}
public static void setSharedElementEnterTransition(final Activity activity, int transition) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP)
return;
activity.getWindow().setSharedElementEnterTransition(TransitionInflater.from(activity).inflateTransition(transition));
}
}
So what are the different parts here: We have two activities. During the transition, four views are transitioned between the activities.
Toolbar: like in the left gif the toolbar doesn't move with the rest of the content.
ListView element View -> becomes the content view of the DetailActivity
StatusBar and NavigationBar Background: If we don't add these views to the set of transitioned views they will fade out and back in during the transition. This however requires to delay the enter transition (see: TransitionHelper.fixSharedElementTransitionForStatusAndNavigationBar)
In the MainActivity the transitioned views are added to the Bundle that is used to start the DetailActivity. Furthermore the transitioned views need to be named (transitionName) in both activities. This can be done in the layout xml as well as programatically.
The default set of transitions, that is used during the shared element transition, affects different aspects of the view(for example: view bounds - see 2). However differences in the elevation of a view are not animated. This is why the presented solution utilizes the custom ElevationTransition.
you can also see this from google documentation:
https://developer.android.com/training/transitions/start-activity
I have an Activity which has a button and by code I draw a Circle, so my Circle class is:
public class Circle extends View {
private float x = 100;
private float y = 100;
private float radius = 50;
public Circle(Context context) {
super(context);
}
protected void onDraw(Canvas canvas){
Paint paint = new Paint();
canvas.drawCircle(x, y, radius, paint);
}
}
And the code of my activity is:
public class AnotherTest extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Circle c = new Circle(this);
setContentView(c);
}
}
But when I invoke setContentView, the button seems to be deleted. Any tips to show the circle and preserve the button?
The button you mentioned is in your activity's layout XML file? If so, could you provide the code? setContentView() will only show the circle. If you want to add the circle to your existing layout you have to add it to a ViewGroup in your Activity's XML.
You could do something like this:
public class AnotherTest extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.anothertest_activity);
}
}
And the (res/layout/)anothertest_activity.xml file could look like this:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout android:id="#+id/content"
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<Button
android:id="#+id/myButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<your.package.Circle
android:id="#+id/myCircle"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
In Android Studio, right below the xml code are two tabs: "Design" and "Text". Switch to "Text" to paste code, switch back to "Design" to position your elements.
If you drag your views, you have an layout XML file. In the Activity you need to set this file as your content view, otherwise you wouldn't see your views. But you can add views dynamically by doing something like this:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout android:id="#+id/content"
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/my_viewgroup"
android:layout_width="match_parent"
android:layout_height="match_parent">
<Button
android:id="#+id/myButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</RelativeLayout>
And in your Activity:
public class AnotherTest extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Still your xml with your button but without circle
setContentView(R.layout.anothertest_activity);
// First find your ViewGroup to add Views to
RelativeLayout viewGroup = (RelativeLayout) findViewById(R.id.my_viewgroup);
// Add a new circle to existing layout
viewGroup.addView(new Circle());
}
}
You also could add everything dynamically:
public class AnotherTest extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ViewGroup viewGroup = new LinearLayout(this);
setContentView(viewGroup);
viewGroup.addView(new Button());
viewGroup.addView(new Circle());
}
}
But I strongly recommend using xml layouts. Might want to take a look at Android Layouts
I have 4 fragments and I want to create a sort of vertical viewpager but I need to keep visible a view of the previous page.
In more details:
Fragment A have a TextView (TV1) on the bottom and other views.
Fragment B have a TextView (TV2) on the bottom and other views.
Fragment C have a TextView (TV3) on the bottom and other views.
I start my Activity, Fragment A occupies the entire layout.
Click on a button -> Fragment A slides up and Fragment B appears but TV1 should still be visibile and fixed on the top of the screen.
Click on a button -> Fragment B slides up and Fragment C appears but TV2 should still be visibile and fixed on the top of the screen (TV2 should replace TV1)...
If I click on TV2 the Fragment B will reappear above the Fragment B.
How can I obtain this behavior?
I finally managed to implement something similar to what you ask about. Here's how it looks:
It might a bit hacky, though that's how I archive it:
First, I needed some TvFragment:
public class TvFragment extends android.support.v4.app.Fragment {
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.tv_fragment, container, false);
TextView textView = (TextView)rootView.findViewById(R.id.tvTextView);
textView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
((OnScrollChanged)getActivity()).onScroll(TvFragment.this);
}
});
return rootView;
}
public void display(int height, String tvTitle, int backgroundColor) {
if (getView() == null) {
return;
}
ViewGroup.LayoutParams params = getView().getLayoutParams();
params.height = height;
getView().setLayoutParams(params);
TextView textView = (TextView)getView().findViewById(R.id.tvTextView);
textView.setText(tvTitle);
getView().setBackgroundColor(backgroundColor);
}
}
And it's tv_fragment.xml:
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:layout_gravity="bottom"
android:id="#+id/tvTextView"
android:gravity="center"
android:textColor="#FFFFFF"
android:textSize="24sp"
android:background="#drawable/textview_backgroud_selector"
android:padding="8dp"
android:layout_margin="#dimen/tv_button_margin"
android:layout_width="match_parent"
android:layout_height="#dimen/tv_button_height" />
</FrameLayout>
And then we need to fill the Activity with our Fragment
Then, we need to have an adapter to fill it with our fragments:
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<ScrollView
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/scrollView"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<fragment
android:id="#+id/fragmentA"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:name="klogi.com.verticalpagination.TvFragment"/>
<fragment
android:id="#+id/fragmentB"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:name="klogi.com.verticalpagination.TvFragment"/>
<fragment
android:id="#+id/fragmentC"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:name="klogi.com.verticalpagination.TvFragment"/>
</LinearLayout>
</ScrollView>
I.e. we keep all three fragments in one ScrollView.
Small helper interface to communicate between fragment and activity:
public interface OnScrollChanged {
void onScroll(Fragment fragment);
}
And last piece is MainActivity class:
public class MainActivity extends AppCompatActivity implements OnScrollChanged {
TvFragment fragmentA;
TvFragment fragmentB;
TvFragment fragmentC;
int bigFragmentHeight;
int smallFragmentHeight;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
getSupportActionBar().hide();
DisplayMetrics metrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(metrics);
bigFragmentHeight = metrics.heightPixels - getStatusBarHeight();
smallFragmentHeight = bigFragmentHeight - getResources().getDimensionPixelSize(R.dimen.tv_button_height) - 2 * getResources().getDimensionPixelSize(R.dimen.tv_button_margin);
fragmentA = (TvFragment)getSupportFragmentManager().findFragmentById(R.id.fragmentA);
fragmentA.display(bigFragmentHeight, "TV1", Color.BLUE);
fragmentB = (TvFragment)getSupportFragmentManager().findFragmentById(R.id.fragmentB);
fragmentB.display(smallFragmentHeight, "TV2", Color.RED);
fragmentC = (TvFragment)getSupportFragmentManager().findFragmentById(R.id.fragmentC);
fragmentC.display(smallFragmentHeight, "TV3", Color.YELLOW);
ScrollView scrollView = (ScrollView)findViewById(R.id.scrollView);
scrollView.setOnTouchListener( new View.OnTouchListener(){
#Override
public boolean onTouch(View v, MotionEvent event) {
return true;
}
});
}
public int getStatusBarHeight() {
int result = 0;
int resourceId = getResources().getIdentifier("status_bar_height", "dimen", "android");
if (resourceId > 0) {
result = getResources().getDimensionPixelSize(resourceId);
}
return result;
}
#Override
public void onScroll(Fragment fragment) {
ScrollView scrollView = (ScrollView)findViewById(R.id.scrollView);
int currentScroll = scrollView.getScrollY();
if (fragment.equals(fragmentA)) {
if (currentScroll == 0) {
scrollView.smoothScrollTo(0, smallFragmentHeight);
} else {
scrollView.smoothScrollTo(0, 0);
}
} else if (fragment.equals(fragmentB)) {
if (currentScroll == smallFragmentHeight) {
scrollView.smoothScrollTo(0, smallFragmentHeight + bigFragmentHeight);
} else {
scrollView.smoothScrollTo(0, smallFragmentHeight);
}
} else if (fragment.equals(fragmentC)) {
// do nothing
}
}
}
What am I doing here - is to disabling "normal" scrolling of the ScrollView and depends on which fragment's button has been clicked - smooth scrolling up or down.
I used also this resources:
dimens:
<resources>
<dimen name="tv_button_height">48dp</dimen>
<dimen name="tv_button_margin">8dp</dimen>
</resources>
colors:
<resources>
<color name="textview_backgroud">#AAAAAA</color>
<color name="textview_backgroud_pressed">#777777</color>
</resources>
and textview_backgroud_selector:
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_pressed="true" android:color="#color/textview_backgroud_pressed"/>
<item android:color="#color/textview_backgroud"/>
</selector>
I've uploaded the complete project into my dropbox - feel free to check it out
That's it! I hope, it helps
I have problem with DialogFragmnt's Width and Height. Here is my class representing DialogFragmetn:
public class RecipeAddDialogFragment extends DialogFragment {
private ArrayList<RecipeDialogItem> recipeDialogItems;
private RecipeAddDialogAdapter recipeDialogAdapter;
private String recipeUniqueId;
private CoordinatorLayout coordinatorLayout;
private RecipeAddDialogFragment recipeDialogFragment;
#Override
public void onStart() {
super.onStart();
if (getDialog() == null) {
return;
}
int dialogWidth = 600;
int dialogHeight = 300;
getDialog().getWindow().setLayout(dialogWidth, dialogHeight);
getDialog().setTitle(getString(R.string.recipe_dialog_title));
}
#Override
public void onCreate(Bundle bundle) {
super.onCreate(bundle);
setStyle(DialogFragment.STYLE_NORMAL, R.style.AppTheme_DialogFragment);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
return inflater.inflate(R.layout.dialog_fragment, container, false);
}
#Override
public void onViewCreated(View view, Bundle savedInstanceState) {
recipeDialogItems = new ArrayList<>();
RecyclerView dialogRecyclerView = (RecyclerView) view.findViewById(
R.id.dialog_recycler_view);
recipeDialogAdapter = new RecipeAddDialogAdapter(getContext(), recipeDialogItems,
R.layout.recipe_dialog_item);
recipeDialogAdapter.setRuidClRdf(recipeUniqueId, coordinatorLayout, recipeDialogFragment);
dialogRecyclerView.setHasFixedSize(true);
dialogRecyclerView.setAdapter(recipeDialogAdapter);
dialogRecyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
fillRecipeDialogArray();
}
private void fillRecipeDialogArray() {
String name = getString(R.string.add_to_favourites);
int icon = R.drawable.ic_heart_48dp;;
RecipeDialogItem dialogItem = new RecipeDialogItem();
dialogItem.setRowIcon(icon);
dialogItem.setRowOption(name);
recipeDialogItems.add(dialogItem);
recipeDialogAdapter.notifyDataSetChanged();
}
public void setReferences(String recipeUniqueId, CoordinatorLayout coordinatorLayout,
RecipeAddDialogFragment recipeDialogFragment) {
this.recipeUniqueId = recipeUniqueId;
this.coordinatorLayout = coordinatorLayout;
this.recipeDialogFragment = recipeDialogFragment;
}
}
Here is .xml which I infalte in this DialogFragment:
<?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:gravity="center|left"
android:padding="16dp"
android:orientation="horizontal"
android:clickable="true"
android:background="?android:attr/selectableItemBackground">
<!-- Option Icon -->
<ImageView
android:id="#+id/recipe_dialog_option_icon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginRight="4dp"
android:tint="#color/primary" />
<!-- Text Option -->
<TextView
android:id="#+id/recipe_dialog_option_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="18sp"
android:textColor="#color/primary_text" />
</LinearLayout>
The problem is that when I set it's size to 600 x 300 it is displayed fine in my 1280x720 device, but for example when my friend displays it on 1920x1080 resolution dialog is wrapped and only title is shown. List is wrapped and is not entire shown. Any idea how can I automaticly set it's size to fit every display and show entire dialog which is wrapped to it's content?
Edit
I have figured out to adjust the width of the DialogFragment to it's content like this:
getDialog().getWindow().setLayout(WindowManager.LayoutParams.WRAP_CONTENT,
WindowManager.LayoutParams.WRAP_CONTENT);
getDialog().setTitle(getString(R.string.recipe_dialog_title));
However height is not working properly :/
In DialogFragment.onResume():
int width = getResources().getDimensionPixelSize(R.dimen.popup_width);
int height = getResources().getDimensionPixelSize(R.dimen.popup_height);
getDialog().getWindow().setLayout(width, height);
In the layout for the dialog:
android:layout_width="match_parent"
android:layout_height="match_parent"
Can take whole screen with:
getDialog().getWindow().setLayout(
getResources().getDisplayMetrics().widthPixels,
getResources().getDisplayMetrics().heightPixels
);
Hope that helps
Found the idea here How to set DialogFragment's width and height?