I'm trying to rotate a ListView inside of a custom popupWindow. Below is my setup:
Here is the popup XML, board_dialog.xml:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/boardll"
android:orientation="vertical"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center">
<ListView
android:id="#+id/boardoptions"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:entries="#array/options_array_board" />
</RelativeLayout>
My custom BoardPopup class:
public class BoardPopup extends PopupWindow {
private static final String TAG = BoardPopup.class.getSimpleName();
Context context;
RelativeLayout ll;
ListView lv;
private OnSubmitListener mListener;
public BoardPopup (Context ctx, OnSubmitListener listener) {
super(ctx);
context = ctx;
mListener = listener;
setContentView(LayoutInflater.from(context).inflate(R.layout.board_dialog, null));
setHeight(WindowManager.LayoutParams.WRAP_CONTENT);
setWidth(WindowManager.LayoutParams.WRAP_CONTENT);
View popupView = getContentView();
setFocusable(true);
lv = (ListView) popupView.findViewById(R.id.boardoptions);
ll = (RelativeLayout) popupView.findViewById(R.id.boardll);
}
public void show(View v) {
showAtLocation(v, Gravity.CENTER, 0, 0);
}
public interface OnSubmitListener {
void valueChanged(String name, String number);
}
public void fixDimensions() {
getContentView().setBackgroundColor(Color.RED); //to highlight views
ll.setRotation(90);
update(292,630); //These numbers are not meant to be constant
}
}
In my activity, showing the popup and I have to override onWindowFocusChanged in order to get post-drawn dimensions for the views inside the popup:
popup = new BoardPopup(c, MainGamePanel.this);
popupJustCreated = true;
popup.show(v);
.
#Override
public void onWindowFocusChanged(boolean hasFocus) {
super.onWindowFocusChanged(hasFocus);
if (popup!=null && popupJustCreated) {
popup.fixDimensions();
popupJustCreated = false;
}
}
If I comment out ll.setRotation(90); and update(292,630); in fixDimensions() then everything looks normal:
If I add in the ll.setRotation(90);:
Finally, if I add in the update(292,630);:
In the final image, why does the layout not fill the popup? What view is that gray area? How can I get this to rotate and resize normally?
Some other things I've tried with no success:
Using LinearLayout instead of RelativeLayout
all different combinations of wrap_content and match_parent
Doing basically the same thing with a custom DialogFragment
I had a very similar issue and just found a workaround. I was using the view rotationX property to rotate items within a RecyclerView and kept seeing strange clipping behaviour like the images above. What worked for me was calling setLayerType on the parent view (a RecyclerView in my case) with the following arguments:
view.setLayerType(View.LAYER_TYPE_SOFTWARE, null)
The default layer type is is LAYER_TYPE_HARDWARE for API >= 14). By the nature of this work around I'd say this is an Android bug.
Related
In my application I use a bottom sheet (from the support library) which works great. Now I would like to animate a layout change while the sheet is dragged up. For this I have created a subclass of BottomSheetCallback (this is normaly an inner class of a Fragment so not all objects used in this calss are initialized here):
public class MyBehavior extends BottomSheetBehavior.BottomSheetCallback {
Transition transition;
float lastOffset = 0;
Scene scene;
public PlayerBehavior() {
TransitionInflater inflater = TransitionInflater.from(getContext());
transition = inflater.inflateTransition(R.transition.player);
//transition.setDuration(300);
scene = fullLayout;
transition.setInterpolator(new Interpolator() {
#Override
public float getInterpolation(float v) {
return lastOffset;
}
});
}
#Override
public void onStateChanged(#NonNull View bottomSheet, int newState) {
if(newState == BottomSheetBehavior.STATE_DRAGGING) {
TransitionManager.go(scene, transition);
}
}
#Override
public void onSlide(View bottomSheet, final float slideOffset) {
scene = (slideOffset > lastOffset) ? smallLayout : fullLayout;
lastOffset = slideOffset;
}
}
As you can see I also created two Scene from different layout files and a custom Transition to animate between the scenes with the TransitionManager. My problem is that the Transition should be based on the slideOffset parameter (in range of 0-1) but the TransitionManager uses the Animation class in the background which is normally time based in Android.
I tried to create the custom Intapolator but this does not work properly. So how can I create a Transition which is based on an external variable and not on time?
Based on your description, I think you are trying to achieve something like google maps bottom sheet behaviour. The layout changes as the bottomsheet is dragged up.
If that is what you are trying to achieve then you don't need to enforce custom animations, as the bottomsheetdialog itself has those animation behaviour when incorporated inside a parent Coordinator Layout.
Here is a sample code of how I'm implementing the same behaviour. It also makes the FloatingActionButton invisible when the bottomsheet is dragged up to full screen size :
Create a bottomsheetdialog that you want to use inside your main layout
public class CustomBottomDialog extends BottomSheetDialogFragment {
String mSomeName;
#Override
public void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// if some arguments are passed from the calling activity
mSomeName = getArguments().getString("some_name");
}
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
View bottomSheet = inflater.inflate(R.layout.bottomsheet_layout, container, false);
// initialise your bottomsheet_layout items here
TextView tvName = bottomSheet.findViewById(R.id.display_name);
tvName.setText(mSomeName);
tvName.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
// do something here
((MainActivity)getActivity()).doSomething();
}
});
return bottomSheet;
}
}
bottomsheet_layout:
<android.support.design.widget.CoordinatorLayout
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.support.design.widget.FloatingActionButton
android:id="#+id/nav"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="#drawable/navigation_tilt_grey"
app:backgroundTint="#color/colorAccent"
app:elevation="3dp"
app:fabSize="normal"
android:layout_marginEnd="#dimen/activity_horizontal_margin"
app:layout_anchor="#+id/live_dash"
app:layout_anchorGravity="top|right" />
<!--BottomSheet-->
<android.support.v4.widget.NestedScrollView
android:id="#+id/live_dash"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#F3F3F3"
android:clipToPadding="true"
app:layout_behavior="android.support.design.widget.BottomSheetBe
havior"
tools:layout_editor_absoluteY="150dp">
<!--Include your items here, the height of all items combined
will take the main screen layout size with animation-->
</android.support.v4.widget.NestedScrollView>
</android.support.design.widget.CoordinatorLayout>
Calling this BottomSheet from your activity:
public void notifyBottomSheet(String somename){
BottomSheetDialogFragment customDialogFragment = new CustomBottomDialog();
Bundle args = new Bundle();
args.putString("some_name", somename);
customDialogFragment.setArguments(args);
customDialogFragment.show(getSupportFragmentManager(), customDialogFragment.getTag());
customDialogFragment.setCancelable(false); // if you don't wish to hide
}
Hope this solves what you are trying to achieve.
To easily slide something off the bottom of the screen, you can use code such as:
final int activityHeight = findViewById(android.R.id.content).getHeight();
cardContainer.animate().yBy(activityHeight - cardContainer.getY()).setDuration(SLIDE_OUT_DURATION);
where cardContainer is the view you are trying to slide off the screen.
See this blog post for the complete example. Note that you can also use translationY instead of yBy. Another, more generic way of doing it is with this code:
public static ViewPropertyAnimator slideOutToBottom(Context ctx, View view) {
final int screenHeight = ctx.getResources().getDisplayMetrics().heightPixels;
int[] coords = new int[2];
view.getLocationOnScreen(coords);
return view.animate().translationY(screenHeight - coords[Y_INDEX]).setDuration(SLIDE_OUT_DURATION);
}
public static ViewPropertyAnimator slideInFromBottom(Context ctx, View view) {
final int screenHeight = ctx.getResources().getDisplayMetrics().heightPixels;
int[] coords = new int[2];
view.getLocationOnScreen(coords);
view.setTranslationY(screenHeight - coords[Y_INDEX]);
return view.animate().translationY(0).setDuration(SLIDE_IN_DURATION).setInterpolator(new OvershootInterpolator(1f));
}
## Translation Animation ##
<?xml version="1.0" encoding="utf-8"?>
<set
xmlns:android="http://schemas.android.com/apk/res/android"
android:interpolator="#android:anim/accelerate_decelerate_interpolator"
android:fillAfter="true"
>
<translate
android:fromYDelta="100%p"
android:toYDelta="-30%p"
android:duration="900" />
</set>
## Main Activity ##
#Override
protected void onResume() {
super.onResume();
Animation am= AnimationUtils.loadAnimation(this,R.anim.fadeout);
tv5.startAnimation(am);
Animation myanim= AnimationUtils.loadAnimation(this,R.anim.translate);
tv1.startAnimation(myanim);
myanim.setStartOffset(500);
Animation animation= AnimationUtils.loadAnimation(this,R.anim.translate);
animation.setStartOffset(1000);
tv2.startAnimation(animation);
Animation an= AnimationUtils.loadAnimation(this,R.anim.translate);
an.setStartOffset(1500);
tv3.startAnimation(an);
Animation ab= AnimationUtils.loadAnimation(this,R.anim.translate);
ab.setStartOffset(2000);
tv4.startAnimation(ab);
Animation ac= AnimationUtils.loadAnimation(this,R.anim.fadein);
ac.setStartOffset(2500);
btn1.startAnimation(ac);
}
I'm not sure if that is what you want but maybe instead of using transition, you can use the function animate() since with this function, you can change all things about your animation (time, visibility etc.).
I'm currently developing an Android App, using a personal SurfaceView and double buffering. However I'm facing a little problem with my code.
In one hand I have an xml view, based on LinearLayout hierarchy. When I instantiate my activity, I set my contentView on this xml. The problem is then that my double buffering don't works anymore. Thread is running but nothing is displayed.
In the other hand, I set my contentView with a new personal SurfaveView element and display works fine. But of course, I cannot access anymore to the other elements of my LinearLayout.
Actually, I would like to set my contentView on my xml view AND keep my display working.
I hope I was clear enough, thank you for your answers!
Here is my activity:
public class MainController extends Activity {
#Override
protected void onCreate(final Bundle p_savedInstanceState) {
super.onCreate(p_savedInstanceState);
setContentView(R.layout.main_controller_activity);
this.drawableView = (MCustomDrawableView) findViewById(R.id.drawingBox);
...
}
...
}
My surface view:
public class MCustomDrawableView extends SurfaceView {
public MCustomDrawableView(Context p_context, AttributeSet p_attributes) {
super(p_context, p_attributes);
this.getHolder().addCallback(new SurfaceHolder.Callback() {
#Override
public void surfaceCreated(final SurfaceHolder p_holder) {
try {
// Instantiating a new thread
thread = new MBufferingThread(getInstance());
thread.start();
} catch (Exception exception) {
exception.printStackTrace();
}
}
#Override
public void surfaceChanged(final SurfaceHolder p_holder, int p_format, int p_width, int p_height) {...}
#Override
public void surfaceDestroyed(final SurfaceHolder p_holder) {...}
});
// Setup drawing options
setupDrawing();
}
...
}
And my xml view:
<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:paddingTop="#dimen/activity_vertical_margin"
android:paddingBottom="#dimen/activity_vertical_margin"
android:background="#color/app_background"
tools:context=".MainController">
<com.iskn.calligraphy.models.draw.MCustomDrawableView
android:id="#+id/drawingBox"
style="#style/DrawingBox" />
<SeekBar
android:id="#+id/brushSize"
style="#style/SizeSeekBar" />
<SeekBar
android:id="#+id/brushOpacity"
style="#style/OpacitySeekBar" />
</LinearLayout>
EDIT:
After further researches and analyses, it appears clearly that:
setContentView(R.layout.main_controller_activity): in this case I get all the elements from my activity, but the MCustomDrawableView display nothing.
setContentView(new MCustomDrawableView(getApplicationContext())): on that case, MCustomDrawableView is working well (it displays what I want), but I don't have the others View from my main_controller_activity
In both cases:
my thread is running and works well.
my drawing function is called as well, with the holder.lockCanvas() and holder.unlockCanvasAndPost(bufferCanvas) methods.
Well, I found a functionnal solution :
I think the problem was coming from the MCustomDrawableView initialization . I created a new instance of that class from the onCreate method , and not from the xml file . Then, I set it to the contentView:
protected void onCreate(Bundle p_savedInstanceState) {
super.onCreate(p_savedInstanceState);
setContentView(R.layout.main_controller_activity);
// Instantiate drawable object & set style properties
this.drawableView = new MCustomDrawableView(getApplicationContext());
FrameLayout.LayoutParams style = new FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT,
FrameLayout.LayoutParams.MATCH_PARENT);
// Add the drawable view to the main_activity
addContentView(this.drawableView, style);
}
But this way raises a new problem. The added view is on the top, which means that all the others View are hided. I solved this with bringToBack(this.drawableView) below method:
private void bringToBack(View p_view) {
// Get parent from the current view
ViewGroup viewGroup = ((ViewGroup) p_view.getParent());
int childrenCount = viewGroup.indexOfChild(p_view);
for(int cpt = 0; cpt < childrenCount; cpt++) {
// Move the child to the top
viewGroup.bringChildToFront(viewGroup.getChildAt(cpt));
}
}
It brings back the provided view to the background position. I also had to set the LinearLayout's alpha to 0.
I'm still open to other solutions!
There is scroll view between header(at the top of screen) and Tabs(Bottom of the screen). I want to know that whether the ImageView which is inside the ScrollView is fully visible or not on phone screen window.
I would suggest to do the following way (the approach is similar to one in this question).
E.g. You have the following xml (I'm not sure what are header and tabs so they are missed):
<ScrollView
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_height="match_parent"
android:layout_width="match_parent"
android:id="#+id/scroller">
<ImageView
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:layout_gravity="center"
android:id="#+id/image"
android:src="#drawable/image001"
android:scaleType="fitXY" />
</ScrollView>
Then activity might look like the following:
public class MyActivity extends Activity {
private static final String TAG = "MyActivity";
private ScrollView mScroll = null;
private ImageView mImage = null;
private ViewTreeObserver.OnGlobalLayoutListener mLayoutListener = new ViewTreeObserver.OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
final Rect imageRect = new Rect(0, 0, mImage.getWidth(), mImage.getHeight());
final Rect imageVisibleRect = new Rect(imageRect);
mScroll.getChildVisibleRect(mImage, imageVisibleRect, null);
if (imageVisibleRect.height() < imageRect.height() ||
imageVisibleRect.width() < imageRect.width()) {
Log.w(TAG, "image is not fully visible");
} else {
Log.w(TAG, "image is fully visible");
}
mScroll.getViewTreeObserver().removeOnGlobalLayoutListener(mLayoutListener);
}
};
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Show the layout with the test view
setContentView(R.layout.main);
mScroll = (ScrollView) findViewById(R.id.scroller);
mImage = (ImageView) findViewById(R.id.image);
mScroll.getViewTreeObserver().addOnGlobalLayoutListener(mLayoutListener);
}
}
In case of small image it will log: image is fully visible.
However, You should be aware about the following inconsistency (as per my understanding): if You have big image, but doing scaling to it (e.g. you set android:layout_width="wrap_content") when it will look scaled, but actual ImageView height will be as full height of the image (and ScrollView will be even scrolling), so adjustViewBounds might be needed. The reason for that behavious is that FrameLayout doesn't care about layout_width and layout_height of childs.
I have a linear layout which holds inner (child) custom image view.
My goal is to set a background selector on the image view, and also set a listener
to the linear layout.
however, I can't make the selector and the listener work together.
I don't know how to handle the events.
If I make a quick tap it is working, but If I drag my finger on the views, the image view
remains in selected state.
Here's the activity which has a reference to the linear layout and its listener.
public class EventDispatchingDemoActivity extends Activity
{
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
LinearLayout linearLayout = (LinearLayout) findViewById(R.id.my_layout);
linearLayout.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v)
{
Toast.makeText(getBaseContext(), "Click", Toast.LENGTH_SHORT).show();
}
});
}
}
Here's the custom view (Its a code I found somewhere, credits for the author)
public class ImageSelector extends ImageView
{
public ImageSelector(Context context, AttributeSet attrs)
{
super(context, attrs);
setBackgroundDrawable(new NavStateListDrawable(getContext(), 0));
setClickable(true);
}
#Override
public boolean onTouchEvent(MotionEvent event)
{
super.onTouchEvent(event);
return false;
}
public class NavStateListDrawable extends StateListDrawable
{
private int level;
public NavStateListDrawable(Context context, int level)
{
this.level = level;
// int stateChecked = android.R.attr.state_checked;
int stateFocused = android.R.attr.state_focused;
int statePressed = android.R.attr.state_pressed;
int stateSelected = android.R.attr.state_selected;
addState(new int[] { stateSelected }, context.getResources().getDrawable(R.drawable.slide_feedback));
addState(new int[] { statePressed }, context.getResources().getDrawable(R.drawable.slide_feedback));
addState(new int[] { stateFocused }, context.getResources().getDrawable(R.drawable.slide_feedback));
addState(new int[] { -stateFocused, -statePressed, -stateSelected }, context.getResources().getDrawable(android.R.color.transparent));
}
#Override
protected boolean onStateChange(int[] stateSet)
{
boolean nowstate = super.onStateChange(stateSet);
try
{
LayerDrawable defaultDrawable = (LayerDrawable) this.getCurrent();
LevelListDrawable bar2 = (LevelListDrawable) defaultDrawable.findDrawableByLayerId(R.id.element_contact_image_selector);
bar2.setLevel(level);
}
catch (Exception exception)
{
}
return nowstate;
}
}
}
And Here's the xml:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:background="#acf1fa"
android:gravity="center"
android:orientation="vertical" >
<!-- outer layout -->
<LinearLayout
android:id="#+id/my_layout"
android:layout_width="200dp"
android:layout_height="200dp"
android:background="#00ff00"
android:gravity="center" >
<!-- inner layout -->
<dor.event.dispathing.ImageSelector
android:id="#+id/inner_layout"
android:layout_width="150dp"
android:layout_height="150dp" />
</LinearLayout>
</LinearLayout>
I Uploaded the source:
http://www.filefactory.com/file/5ikfwyk9j5bx/n/EventDispatchingDemo.rar
Thanks in advance for your help!
I remember having a similar issue when I was writing my application.
1) Are you trying to capture if a row in your listview is clicked? I think you want `listView.setonItemClickListener' which will capture clicks for each individual row as opposed to the entire listview.
2) I believe when you have a listview with clickable items inside it, the clickable items inside it take priority in terms of capturing clicks. More than taking priority, I believe they block any click events from being sent back to the parent's event handlers.
I believe a somewhat working solution is to set the focusability of those clickable items to false. Once those clickable items are not longer focusable, the listview row will get focus back. The other solution is to work with the descendantFocusability property which controls who (Entire row or Image) gets first crack and picking up events triggered within the row. Here are some other questions that seem to have what you need:
How to fire onListItemClick in Listactivity with buttons in list?
Android ListView with clickable items in it's rows causes problems continuing scrolling
I have listview that stores the communication history of a person. I have one header inside a listview that acts as a message editor with a edit text and a send button.
When a user types something and press send button the messages adds to the communication list and editor gets empty.
What I want is when user press the send button, the editor should become invisible and Item should be added to the listview. After that the editor should come gradually from the top giving the feel that its moving the items below.
I have implemented a translate animation on the header but what it does is it makes the space for it by pushing the items down and then gradually fills the space which I dont want.
I used the negative margin trick which is explained in this question but It didn't work for me. As we cant use layout params other that AbsListView.LayoutParam for the headers. I tried setting Other params but while animating It gives me ClassCastException. I tracked the exception and its due to code written inside ListView they are trying to cast these params with AbsListView.LayoutParams inside clearRecycledState() method.
Or Is there a way to apply layout params that supports margin on a listview-header.
the code
public class PageListView extends ListView {
private Application app;
private CommListAdapter listAdapter;
private MessageEditorHeader messageEditorHeader;
private MessageItemLongClick mInterface;
private Handler handler;
public ProfilePageListView(Application app, MessageItemLongClick mInterface) {
super(app);
this.app = app;
this.mInterface = mInterface;
this.handler = new Handler();
setupView();
}
public void applyData(ProfileData data){
listAdapter.applyData(data.getUser());
// some other business logic
}
private void setupView() {
messageEditorHeader = new MessageEditorHeader(app);
addHeaderView(messageEditorHeader);
listAdapter = new CommListAdapter(app, mInterface);
setAdapter(listAdapter);
setDivider(null);
setScrollingCacheEnabled(false);
tAnimation = new TranslateAnimation(0.0f, 0.0f, -90.0f, 0.0f);
tAnimation.setZAdjustment(-1);
tAnimation.setDuration(1500);
}
// this gets called whenever the communication gets added to the listview.
public void onNewCommunication(Communication lc) {
listAdapter.onNewCommunication();
if(lc != null && lc.isOutgoing() && !lc.getType().isCall()){
getMessageEditor().startNewMessage();
messageEditorHeader.setVisibility(VISIBLE); // this is overriden method here I m toggling the height 1px and WRAP_CONTENT
messageEditorHeader.startAnimation(tAnimation);
}
}
// few more methods are there.
}
heres the code of message editor
public class MessageEditorHeader extends RelativeLayout {
private MessageEditor msgEditor;
public MessageEditorHeader(AppteraApplication context) {
super(context);
msgEditor = new MessageEditor(context); // Its a relative layout containing edit text and the send button
addView(msgEditor);
}
public MessageEditor getMsgEditor() {
return msgEditor;
}
public void setProgress(int progress){
msgEditor.setProgress(progress);
}
#Override
public void setVisibility(int visibility) {
this.visibility = visibility;
if (visibility == View.VISIBLE) {
ListView.LayoutParams params = new ListView.LayoutParams(ListView.LayoutParams.FILL_PARENT, ListView.LayoutParams.WRAP_CONTENT);
setLayoutParams(params);
}
else {
ListView.LayoutParams params = new ListView.LayoutParams(ListView.LayoutParams.FILL_PARENT, 1);
setLayoutParams(params);
}
}
}
Have you thought about a different approach instead? Maybe you can just put the editor view at the top of the list, but outside the screen, and then use smoothScrollToPosition to transition in. So in reality you're just scrolling the list, but the effect could be what you're looking for.