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.
Related
I'd like to use the swipe(onFling) feature of android gestures. I have some adjacent pictures to
chancge into an other picture, in case of swiping.(Just like i demonstrated on the picture)
It should work regardless, which direction the player swipe his/her finger.
Could you give me any link? Or any idea which components should i use?
Since your gesture appears to apply the premise that it must:
Gesture must include all adjacent views.Gesture has a direct linear begginning and endGesture is a single movementGesture does not conflict with other similar gestures
You might want to read on "MotionEvent", and the onTouch listener for views.
A single flag private static View beganOn; on the parent class (I am supposing an Activity). Then:
public void onTouch(View v, MotionEvent m){
if(beganOn!=null){
begaOn = v;
return;
} else {
// Where the view Tag, is an Integer to state what number it is in the sequence.
doSelectionOfViews(beganOn.getTag(),v.getTag());
begaOn = null;
}
}
override the onfling() method of the Gesture Detector. You will be able to get the Swipe direction Under this. Now take two counters for both direction and increment it(i.e count++) in the Right/left swipe and vice versa. Below I am posting the code by which you will be able to create that circular indicator. Whichever you want to make highlighted, You need to pass the index only.
public void updateIndicator(int currentPage) {
image_indicator.removeAllViews();
DotsScrollBar.createDotScrollBar(this, image_indicator, currentPage, 5);
}
Here image_indicator is an linear layout defined in xml.
public static class DotsScrollBar
{
LinearLayout main_image_holder;
public static void createDotScrollBar(Context context,
LinearLayout main_holder, int selectedPage, int count)
{
for (int i = 0; i < count; i++) {
ImageView dot = null;
dot = new ImageView(context);
LinearLayout.LayoutParams vp = new LinearLayout.LayoutParams(
LinearLayout.LayoutParams.WRAP_CONTENT,
LinearLayout.LayoutParams.WRAP_CONTENT);
vp.setMargins(8, 8, 8, 8);
dot.setLayoutParams(vp);
if (i == selectedPage) {
try
{
dot.setImageResource(R.drawable.page_hint_pre);
}
catch (Exception e)
{
}
} else
{
dot.setImageResource(R.drawable.page_hint_def);
}
main_holder.addView(dot);
}
main_holder.invalidate();
}
}
Pass the index in the upDateIndicator() method to make that particular incator highlighted.
I'm using the TransitionManager.beginDelayedTransition outlined here
to animate two views swapping positions within a RelativeLayout. I do this by simply swapping the RelativeLayout.LayoutParams of two the two views.
My question is how do I monitor the animation that is automatically created and executed by TransitionManager without having to create my own custom Transitions. I need to detect when the animation has ended so that I can make a change to the views that have been swapped.
Below is the method that swaps the two views. CollageCanvasAperture is an extension of View and mApertureGroup is the RelativeLayout that holds these views.
private void shuffle(int fromApertureInd, int toApertureInd) {
final CollageCanvasAperture fromV = (CollageCanvasAperture) mApertureGroup.getChildAt(fromApertureInd);
final CollageCanvasAperture toV = (CollageCanvasAperture) mApertureGroup.getChildAt(toApertureInd);
if (null == fromV || null == toV) {
return;
}
TransitionManager.beginDelayedTransition(mApertureGroup);
RelativeLayout.LayoutParams fromLP = (RelativeLayout.LayoutParams) fromV.getLayoutParams();
RelativeLayout.LayoutParams toLP = (RelativeLayout.LayoutParams) toV.getLayoutParams();
fromV.setLayoutParams(toLP);
toV.setLayoutParams(fromLP);
}
I've done a few hours of searching on here and combing through the TransitionManager code but can't see how to detect changes. I'd prefer to be able to detect the animation end within the CollageCanvasAperture but can't see any relevant listeners to apply.
I guess I could provide the view with the destination LayoutParams before the animation is executed and then the view can listen for size & location changes until they match...?
So it turned out it's quite straightforward to add a listener to these 'automatic' transitions.
Instead of using: TransitionManager.beginDelayedTransition(mApertureGroup);
you need to work out which automatic Transition is being using when you invoke beginDelayedTransition and call TransitionManager.go() instead.
My view transitions were using the ChangeBounds Transition. Once that was know all I had to do was:
ChangeBounds mySwapTransition = new ChangeBounds();
mySwapTransition.addListener(new Transition.TransitionListener() {
#Override
public void onTransitionStart(Transition transition) { }
#Override
public void onTransitionEnd(Transition transition) { }
#Override
public void onTransitionCancel(Transition transition) {}
#Override
public void onTransitionPause(Transition transition) { }
#Override
public void onTransitionResume(Transition transition) { }
});
TransitionManager.go(new Scene(mApertureGroup), mySwapTransition);
So in my android app I have a HorizontalScrollView that will display images. All images are downloaded an added to the View before it is avalible to the user. However when it does apear I want each image to animate in seperatly. Ive tried a LayoutTransition object attached to my layout with this code to show the views:
transition = new LayoutTransition();
transition.setStagger(LayoutTransition.APPEARING, 500);
transition.setDuration(1000);
transition.setAnimateParentHierarchy(true);
for (int i = 0; i < mGallery.getChildCount(); i++) {
mGallery.getChildAt(i).setVisibility(View.VISIBLE);
transition.showChild(mGallery, mGallery.getChildAt(i));
}
I have also tried this method using the the AnimationEndListener, and a recursive animateView() method
private void animateView(final int index) {
if (mGallery.getChildAt(index) == null)
return;
final View child = mGallery.getChildAt(index);
final Animation an = AnimationUtils.loadAnimation(mRootView.getContext(), R.anim.slideup);
AnimationEndListener listener = new AnimationEndListener() {
#Override
public void onAnimationEnd(Animation animation) {
animateView(index + 1);
}
};
an.setAnimationListener(listener);
child.setAnimation(an);
child.setVisibility(View.VISIBLE);
an.start();
}
The first method is preferable however it always animates in all my images at the same time. The second method kind of works, however it will apply the animation to the first view and the all subsequent views will appear in sequence without the animation playing.
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.
I'm trying to create a custom surfaceview where every time the view is on the screen the view will start playing a video on its own. I was wondering what method within View is notified when a view is displayed on the UI and seen by the user. I'm using a viewpager so the SurfaceCreated doesn't work because views are created before they are displayed on the screen.
How to start a video automatically in a view pager when it comes on the screen
This was the underlying problem. The OP, wisely, wanted to try and isolate the point where it, in a sense "comes on to the screen". Problem is that this can mean many things:
When I first heard the question, I thought a good case would be onAttachedToWindow - see the docs. For people reading this question based on its original title, this is what you want.
The view is inflated and created in the Activity's onCreate in most cases (e.g. if you've used setContentView).
The OP had had no luck with surfaceCreated callbacks either. So we considered in the comments above whether the OP would be interested in the three draw stages layout, measure and draw. There are two stages to actually "putting a view on the screen" in android - the measuring, and the layout pass- see here.
Problem would be that it turned out that the OP was animating his view onto the screen, so the question became how do you tell when a view "arrives" on the screen after animation.
The important point is: you actually wanted to detect a stage much much later in the drawing process, which is understandable! Animation works by many calls to invalidate which in turn require many draws for that view's Canvas - so the stage at which you want to play the video is by no means when the view is first displayed in the UI.
Solution for this particular scenario.
Use animation listeners on your ViewAnimator instances (e.g. ViewPager). To not have to bother with them in teh activity, I would roll your own view, and then use the Adapter type patterns Android is so fond of to manage constantly changing data:
a very hastily written implementation would be:
public class VideoStartingViewFliper extends ViewFlipper {
private final Animation fromRight;
private final Animation toLeft;
private final Animation fromLeft;
private final Animation toRight;
private VideoViewAdapter mAdapter;
public VideoStartingViewFliper(final Context context, final AttributeSet attrs) {
super(context, attrs);
fromRight = new YourChoiceOfAnimation();
fromRight.setAnimationListener(videoStartingAnimationListener);
toLeft = new YourChoiceOfAnimation();
toLeft.setAnimationListener(videoStartingAnimationListener);
fromLeft = new YourChoiceOfAnimation();
fromLeft.setAnimationListener(videoStartingAnimationListener);
toRight = new YourChoiceOfAnimation();
toRight.setAnimationListener(videoStartingAnimationListener);
}
static interface VideoViewAdapter {
public String getVideoPath(int childId);
}
public void setVideoViewAdapter(final VideoViewAdapter adapter) {
mAdapter = adapter;
}
// or even call this showNextVideo and don't override!
#Override
public void showNext() {
setInAnimation(fromRight);
setOutAnimation(toLeft);
super.showNext();
}
#Override
public void showPrevious() {
setInAnimation(fromLeft);
setOutAnimation(toRight);
super.showPrevious();
}
private final AnimationListener videoStartingAnimationListener = new AnimationListener() {
#Override
public void onAnimationStart(final Animation animation) {
final VideoView video = ((VideoView) getCurrentView());
video.stopPlayback();
}
#Override
public void onAnimationRepeat(final Animation animation) {
}
#Override
public void onAnimationEnd(final Animation animation) {
final VideoView video = ((VideoView) getCurrentView());
// check null here!
video.setVideoPath(mAdapter.getVideoPath(getCurrentView().getId()));
video.start();
}
};
}
Hope this helps.