How to create a vertical pagination with a fixed view - android

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

Related

How to implement same transitions?

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

Make the bitmap get over ImageView borders

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:

White screen showing up while calling fragment

I am new to android. So, I have been playing with fragments to understand how they work.
Firstly, I made a calciKeyboard class which extends Fragment
Code:
public class calciKeyboard extends Fragment {
#Nullable
Button b;
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup container, Bundle savedInstanceState) {
GridLayout gridLayout=(GridLayout) inflater.inflate(R.layout.calci_keyboard,container,false);
for(int i=0;i<gridLayout.getChildCount();i++){
b=(Button)gridLayout.getChildAt(i);
if (b != null) {
b.setBackground(getResources().getDrawable(R.drawable.button_dark_gradient));
}
}
return gridLayout;
}
}
calciKeyboard.xml
<?xml version="1.0" encoding="utf-8"?>
<GridLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:rowCount="4"
android:columnCount="4"
>
Buttons inside....
</GridLayout>
advance_mix
public class advance_mix extends Activity {
#Override
protected void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.advance_length);
}
}
advance_length
<?xml version="1.0" encoding="utf-8"?>
<GridLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<fragment android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:name="com.example.tilak.myfirstapplication.calciKeyboard"
android:id="#+id/calciKeyboard" />
</GridLayout>
go_advance.java
findViewById(R.id.redirectLength).setOnClickListener(
new View.OnClickListener() {
#Override
public void onClick(View view) {
Intent i=new Intent(go_advance.this,advance_mix.class);
startActivity(i);
}
}
);
go_advance class contains the intent which starts the advance_mix activity, which further lays out the advance_length layout file.
The problem is when redirect_length button is clicked it shows me a white screen.
Why is it happening?
You should return View .
View getRootView = inflater.inflate(R.layout.calci_keyboard,container,false);
GridLayout gridLayout=(GridLayout)getRootView.findViewById(R.id._gridLAYOUT);
for(int i=0;i<gridLayout.getChildCount();i++){
b=(Button)gridLayout.getChildAt(i);
if (b != null) {
b.setBackground(getResources().getDrawable(R.drawable.button_dark_gradient));
}
return getRootView ;
XML
<GridLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/_gridLAYOUT">
<fragment android:layout_width="match_parent" //Change this to match_parent
android:layout_height="match_parent" //Change this to match_parent
android:name="com.example.tilak.myfirstapplication.calciKeyboard"
android:id="#+id/calciKeyboard" />
</GridLayout>
OK. Try to use "android.support.v4.app.Fragment" rather than "android.app.Fragment" and second think 'Button b;' declare before #Nullable

Adjusting DialogFragment Width and Height

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?

ViewPager with fixed width childs

I need to design a ViewPager which able to pass childs with fixed width (e.g childs with 700dp width), Unfortunately the current version of ViewPager will automatically makes all childrens width to MATCH_PARENT, is there any way to add this functionality to ViewPager?
My ViewPager layout:
<?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="wrap_content"
android:orientation="vertical" >
<android.support.v4.view.ViewPager
android:id="#+id/some_id"
android:layout_width="match_parent"
android:layout_height="300dp"
android:overScrollMode="never" />
</LinearLayout>
ViewPager childs layout:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/banner_main_layout_container"
android:layout_width="700dp"
android:layout_height="match_parent"
android:background="#fff"
android:gravity="center"
android:orientation="vertical" >
<ImageView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:src="some images"/>
</LinearLayout>
Thanks in Advance...
It is possible to scale the pages within the ViewPager with FragmentPagerAdapter.getPageWidth. You will need a custom FragmentPagerAdapter. If you return a number between 0 and 1, the pages are scaled down, width > 1 scales pages up accordingly. But this is not really good, because you can't scroll the image within the up-scaled page.
If you wrap the ImageView in a HorizontalScrollView, things are a bit better, you can scroll the images within pages, but the swipe gesture between pages is caught by the HorizontalScrollView if you are not very fast. See this video.
So the solution is truly to use a custom HorizontalScrollView (see InterceptingHorizontalScrollView) which disallows intercepting the onTouch event, but also allows it when the User scrolls to the end (See overidden onOverScrolled). See this video or the image below for the difference.
EDIT You don't need to override onInterceptTouchEvent, because HorizontalScrollView intercepts them by default (so scrolling the image has higher priority than paging.)
Finally, here's all the code:
MainActivity.java
public class MainActivity extends Activity {
private ViewPager mViewPager;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// Set up the ViewPager
mViewPager = (ViewPager) findViewById(R.id.pager);
mViewPager.setPageMargin(30);
mViewPager.setAdapter(new ImagePagerAdapter(getFragmentManager()));
}
private class ImagePagerAdapter extends FragmentPagerAdapter {
public ImagePagerAdapter(FragmentManager fm) {
super(fm);
}
#Override
public Fragment getItem(int i) {
switch(i) {
case 0:
return ImageFragment.newInstance(R.drawable.forest1);
case 1:
return ImageFragment.newInstance(R.drawable.forest2);
case 2:
return ImageFragment.newInstance(R.drawable.forest3);
default:
return ImageFragment.newInstance(R.drawable.ic_launcher);
}
}
#Override
public int getCount() {
return 3;
}
#Override
public float getPageWidth(int position)
{
// Here it is possible to scale page width
return super.getPageWidth(position);
}
}
}
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">
<android.support.v4.view.ViewPager
android:id="#+id/pager"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</RelativeLayout>
ImageFragment.java
public class ImageFragment extends Fragment {
private static final String ARG_PARAM1 = "image_resid";
private int mImageResId;
public static ImageFragment newInstance(int image_resid) {
ImageFragment fragment = new ImageFragment();
Bundle args = new Bundle();
args.putInt(ARG_PARAM1, image_resid);
fragment.setArguments(args);
return fragment;
}
public ImageFragment() {
// Required empty public constructor
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (getArguments() != null) {
mImageResId = getArguments().getInt(ARG_PARAM1);
}
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.fragment_image, container, false);
ImageView imageView = (ImageView)v.findViewById(R.id.imageView);
imageView.setImageResource(mImageResId);
return v;
}
}
fragment_image.xml
<com.gyebro.viewpagertest.InterceptingHorizontalScrollView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="600dp"
android:layout_height="match_parent"
tools:context="com.gyebro.viewpagertest.ImageFragment">
<ImageView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:adjustViewBounds="true"
android:id="#+id/imageView"
android:src="#drawable/forest1" />
</com.gyebro.viewpagertest.InterceptingHorizontalScrollView>
InterceptingHorizontalScrollView.java
public class InterceptingHorizontalScrollView extends HorizontalScrollView {
public InterceptingHorizontalScrollView(Context context) {
super(context);
}
public InterceptingHorizontalScrollView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public InterceptingHorizontalScrollView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
/*#Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
if (getParent() != null) {
switch (ev.getAction()) {
case MotionEvent.ACTION_MOVE:
getParent().requestDisallowInterceptTouchEvent(true);
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
getParent().requestDisallowInterceptTouchEvent(false);
break;
}
}
return super.onInterceptTouchEvent(ev);
}*/
#Override
protected void onOverScrolled (int scrollX, int scrollY, boolean clampedX, boolean clampedY) {
super.onOverScrolled(scrollX,scrollY,clampedX,clampedY);
// if clampedX == true, we've reached the end of the HorizontalScrollView so
// allow parent to intercept
if(clampedX) {
Log.d("InterceptingHorizontalScrollView", "Reached the end, allowing interception");
getParent().requestDisallowInterceptTouchEvent(false);
}
}
}
What you really want here is a HorizontalScrollView inside of a ViewPager. This requires custom touch handling, so you'll want to use something like this class: InterceptingHorizontalScrollView
To make InterceptingHorizontalScrollView work in a ViewPager, you'll have to override onOverScrolled:
#Override
protected void onOverScrolled (int scrollX, int scrollY, boolean clampedX, boolean clampedY) {
super.onOverScrolled(scrollX,scrollY,clampedX,clampedY);
if(clampedX) {
getParent().requestDisallowInterceptTouchEvent(false);
}
}
Thanks to Gyebro for this tip.^
Your ViewPager child layout would look like this:
<com.tumblr.widget.InterceptingHorizontalScrollView
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<LinearLayout
android:id="#+id/banner_main_layout_container"
android:layout_width="700dp"
android:layout_height="match_parent"
android:background="#fff"
android:gravity="center"
android:orientation="vertical" >
<ImageView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:src="some images"/>
</LinearLayout>
</com.tumblr.widget.InterceptingHorizontalScrollView>
You can either override PagerAdapter´s getWidth method and if this does not help, look at this:
http://commonsware.com/blog/2012/08/20/multiple-view-viewpager-options.html
and most importantly try this example, it works great!
Just go along the whole example.
The ViewPager children will/should always match it's parent width.
Furthermore it sounds like a bad idea to use a 700dp width ImageView. What would that look like in portrait mode?
If you don't want to make the ViewPager itself smaller, i.e. you want the ImageViews to be swiped from the absolute side of the screen, you have to make the items appear smaller.
That imitation could be done by creating 2 additional LinearLayouts to act as spacers. Then it would appear as if your item has a specific width.
Here's an example (with a TextView):
<LinearLayout 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"
android:orientation="horizontal">
<LinearLayout
android:background="#FFF"
android:layout_weight="3"
android:layout_width="0dp"
android:layout_height="match_parent"/>
<TextView
android:layout_weight="10"
android:background="#333"
android:textColor="#FFF"
android:textSize="50sp"
android:gravity="center"
android:text="HELLO"
android:layout_width="0dp"
android:layout_height="match_parent" />
<LinearLayout
android:background="#FFF"
android:layout_weight="3"
android:layout_width="0dp"
android:layout_height="match_parent"/>
</LinearLayout>
And that would look like this:
Set the page margin of the view pager to a negative value. This will force the pages to push into the the view pager. Be warned, it will also cause overlap so you'll see part of the next/previous element in the view pager.

Categories

Resources