Andy.Zhao
Andy.Zhao

Reputation: 258

LinearLayout addView doesn't work

I put addView() in onTouchEvent() method,but it doesn't work. What's the problem? When I put the addView in the construction function it works properly.


onTouchEvent() works properly. The main problem is the new addview doesn't visible. But it really added to the parent view.

setOrientation(LinearLayout.VERTICAL) is already added in the construction function.
This is the code:

public class RecyclerViewRefresh extends LinearLayout{
private static final String LOG_TAG=RecyclerViewRefresh.class.getSimpleName();
private static final int INVALID_POINTER=-1;
//Default offset in dips from the top of the view to where the progress
//spinner should stop
private static final int DEFAULT_CIRCLE_TARGET=64;
private static final float DRAG_RATE=.5f;

private Context context;
private View headerView,footerView,thisView;
private View mTarget; //the target of the gesture
private ImageView arrowIv;
private TextView refreshTv;
private ProgressBar progressBar,footerProgressBar;
private OnPullToRefresh refreshListener=null;
private OnDragToLoad loadListener=null;
float startY=0;

private int headerHeight=0,currentHeaderHeight=0,currentFooterHeight=0;
private boolean mReturningToStart;
private boolean mRefreshing=false;
private boolean mNestedScrollInProgress;
private int mCurrentTargetOffsetTop;
protected int mOriginalOffsetTop;
private boolean mIsBeingDragged;
private boolean mIsBeingPullUp;
private boolean isAddFooter=false;
private int mActivePointerId=INVALID_POINTER;
private float mInitailDownY;
private int mTouchSlop;
private float mTotalDragDistance=-1;
private float mInitialMotionY;
private float mSpinnerFinalOffset;
private boolean updateHeader=true;
private Handler handler=new Handler();
private Timer timer;

public RecyclerViewRefresh(Context context) {
    super(context);
    initView(context);
}

public RecyclerViewRefresh(Context context, AttributeSet attrs) {
    super(context, attrs);
    initView(context);
}

public RecyclerViewRefresh(Context context, AttributeSet attrs, int defStyleAttr) {
    super(context, attrs, defStyleAttr);
    initView(context);
}
private void initView(Context context)
{
    this.setOrientation(LinearLayout.VERTICAL);
    this.context=context;
    thisView=this;
    mTouchSlop= ViewConfiguration.get(context).getScaledTouchSlop();
    headerView=LayoutInflater.from(context).inflate(R.layout.header_layout,null);
    footerView=LayoutInflater.from(context).inflate(R.layout.footer_layout,null);
    measureView(headerView);
    measureView(footerView);
    arrowIv=(ImageView)headerView.findViewById(R.id.arrow);
    refreshTv=(TextView)headerView.findViewById(R.id.tip);
    progressBar=(ProgressBar)headerView.findViewById(R.id.progress);
    headerHeight=headerView.getMeasuredHeight();
    currentHeaderHeight=headerHeight;
    LinearLayout.LayoutParams lp=new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
            headerView.getMeasuredHeight());
    this.addView(headerView,lp);
    setTopHeader(headerHeight);

    final DisplayMetrics metrics=getResources().getDisplayMetrics();
    mSpinnerFinalOffset=DEFAULT_CIRCLE_TARGET*metrics.density;
    mTotalDragDistance=mSpinnerFinalOffset;
}
/**
 * 通知父布局,占用的宽,高;
 *
 * @param view
 */
private void measureView(View view) {
    ViewGroup.LayoutParams p = view.getLayoutParams();
    if (p == null) {
        p = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
                ViewGroup.LayoutParams.WRAP_CONTENT);
    }
    int width = ViewGroup.getChildMeasureSpec(0, 0, p.width);
    int height;
    int tempHeight = p.height;
    if (tempHeight > 0) {
        height = MeasureSpec.makeMeasureSpec(tempHeight,
                MeasureSpec.EXACTLY);
    } else {
        height = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
    }
    view.measure(width, height);
}
private void setTopHeader(int height)
{
    if(Build.VERSION.SDK_INT>=Build.VERSION_CODES.HONEYCOMB)
    {
        this.setY(-height);
    }else{
        LayoutParams lp=new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,height);
        lp.topMargin=-height;
        this.setLayoutParams(lp);
    }
    headerView.invalidate();
}

/**
 * Set the listener to be notified when a refresh is triggered via the
 * pull gesture.
 * @param listener
 */
public void setOnPullToRefresh(OnPullToRefresh listener)
{
    this.refreshListener=listener;
}

/**
 * Set the listener to be notified when a load is triggered via the
 * drag gesture
 * @param listener
 */
public void setOnDragToLoad(OnDragToLoad listener)
{
    this.loadListener=listener;
}


private void ensureTarget(){
    if(mTarget==null){
        for(int i=0;i<getChildCount();i++)
        {
            View child=getChildAt(i);
            if(child instanceof RecyclerView)
            {
                mTarget=child;
                break;
            }
        }
    }
}

/**
 * @return Whether it is possible for the child view of this layout to
 * scroll up.Override this if the child view is a custom view.
 */
public boolean canChildScrollUp(){
    if(mTarget==null)
    {
        ensureTarget();
    }
    if(Build.VERSION.SDK_INT<14)
    {
        if(mTarget instanceof AbsListView)
        {
            final AbsListView absListView=(AbsListView)mTarget;
            return absListView.getChildCount()>0
                    &&(absListView.getFirstVisiblePosition()>0
                    ||absListView.getChildAt(0).getTop()<absListView.getPaddingTop());
        }else{
            return ViewCompat.canScrollVertically(mTarget,-1)|| mTarget.getScrollY()>0;
        }
    }else{
        return ViewCompat.canScrollVertically(mTarget,-1);
    }
}

@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
    ensureTarget();
    final int action=MotionEventCompat.getActionMasked(ev);

    if(mReturningToStart && action == MotionEvent.ACTION_DOWN){
        mReturningToStart = false;
    }
    if(!isEnabled() || mReturningToStart || (canChildScrollUp()&&!ifLastItemVisible())
            ||mRefreshing || mNestedScrollInProgress){
        return false;
    }

    switch (action){
        case MotionEvent.ACTION_DOWN:
            setTargetOffsetTopAndBottom(mOriginalOffsetTop-headerView.getTop(),true);
            mActivePointerId=MotionEventCompat.getPointerId(ev,0);
            mIsBeingDragged=false;
            mIsBeingPullUp=false;
            final float initialDownY=getMotionEventY(ev,mActivePointerId);
            if(initialDownY==-1){
                return false;
            }
            mInitailDownY=initialDownY;
            updateHeader=true;
            break;
        case MotionEvent.ACTION_MOVE:
            if(mActivePointerId==INVALID_POINTER){
                Log.e(LOG_TAG, "Got ACTION_MOVE event but don't have an active pointer id.");
                return false;
            }
            final float y=getMotionEventY(ev,mActivePointerId);
            if(y==-1){
                return false;
            }
            final float yDiff=y-mInitailDownY;
            if(yDiff>mTouchSlop && !mIsBeingDragged){
                mInitialMotionY=mInitailDownY+mTouchSlop;
                mIsBeingDragged=true;
            }
            if(yDiff<-mTouchSlop&&!mIsBeingPullUp&&ifLastItemVisible()&&!isAddFooter)
            {
                Log.d(LOG_TAG,"pullUp");
                mInitialMotionY=mInitailDownY+mTouchSlop;
                mIsBeingPullUp=true;
                return true;
            }
            if(ifLastItemVisible()&&yDiff>mTouchSlop)
            {
                isAddFooter=false;
                return false;
            }
            break;
        case MotionEventCompat.ACTION_POINTER_UP:
            onSecondaryPointerUp(ev);
            break;
        case MotionEvent.ACTION_UP:
        case MotionEvent.ACTION_CANCEL:
            mIsBeingDragged=false;
            mActivePointerId=INVALID_POINTER;
            break;
    }
    return mIsBeingDragged;
}
private boolean ifLastItemVisible()
{
    final RecyclerView recyclerView=(RecyclerView)mTarget;
    LinearLayoutManager manager=(LinearLayoutManager)recyclerView.getLayoutManager();
    if((manager.findLastVisibleItemPosition()+1)==manager.getItemCount())
    {
        return true;
    }
    return false;
}

private float getMotionEventY(MotionEvent ev,int activePointerId){
    final int index=MotionEventCompat.findPointerIndex(ev,activePointerId);
    if(index<0){
        return -1;
    }
    return MotionEventCompat.getY(ev,index);
}
private void setTargetOffsetTopAndBottom(int offset,boolean requiresUpdate){
    if(currentHeaderHeight>-5)
    {
        currentHeaderHeight-=offset;
        this.setY(-currentHeaderHeight);
        mCurrentTargetOffsetTop=this.getTop();
        if(requiresUpdate && Build.VERSION.SDK_INT<11){
            invalidate();
        }

        if(currentHeaderHeight<0)
        {
            if(updateHeader){
                updateHeader=false;
                refreshTv.setText(getResources().getText(R.string.releasetorefresh));
                RotateAnimation animation=new RotateAnimation(0,180,
                        Animation.RELATIVE_TO_SELF,0.5f,Animation.RELATIVE_TO_SELF,0.5f);
                animation.setDuration(800);
                animation.setFillAfter(true);
                arrowIv.startAnimation(animation);
            }
            Log.d(LOG_TAG,"top="+this.getY());
        }
    }

}

private void onSecondaryPointerUp(MotionEvent ev){
    final int pointerIndex=MotionEventCompat.getActionIndex(ev);
    final int pointerId=MotionEventCompat.getPointerId(ev,pointerIndex);
    if(pointerId==mActivePointerId){
        //This was our active pointer going up. Choose a new
        //active pointer and adjust accordingly.
        final int newPointerIndex=pointerIndex==0?1:0;
        mActivePointerId=MotionEventCompat.getPointerId(ev,newPointerIndex);
    }
}
@Override
public boolean onTouchEvent(MotionEvent event)
{
    final int action=MotionEventCompat.getActionMasked(event);
    int pointerIndex=-1;

    if(mReturningToStart&&action==MotionEvent.ACTION_DOWN){
        mReturningToStart=false;
    }
    if(!isEnabled() || mReturningToStart
            || (canChildScrollUp()&&!ifLastItemVisible()) || mNestedScrollInProgress){
        //Fail fast if we're not in a state where a swipe is possible
        return false;
    }
    switch(action){
        case MotionEvent.ACTION_DOWN:
            mActivePointerId=MotionEventCompat.getPointerId(event,0);
            mIsBeingDragged=false;
            break;
        case MotionEvent.ACTION_MOVE:{
            pointerIndex=MotionEventCompat.findPointerIndex(event,mActivePointerId);
            if(pointerIndex<0){
                Log.e(LOG_TAG, "Got ACTION_MOVE event but have an invalid active pointer id.");
                return false;
            }
            final float y=MotionEventCompat.getY(event,pointerIndex);
            final float overscrollTop=(y-mInitialMotionY)*DRAG_RATE;
            Log.d(LOG_TAG,"move overscroll="+overscrollTop+" y="+y+" mInitialMotionY="+mInitialMotionY+"  mIsBeingDragged="+(mIsBeingDragged?"true":"false")
                +"  mIsBeingPullUp="+(mIsBeingPullUp?"true":"false"));
            if(mIsBeingDragged){
                if(overscrollTop>0){
                    moveSpinner(overscrollTop);
                }else{
                    return false;
                }
            }
            if(mIsBeingPullUp&&!isAddFooter){
                Log.d(LOG_TAG,"isAddFooter="+(isAddFooter?"true":"false"));
                if(overscrollTop<0&&!isAddFooter){
                    isAddFooter=true;
                    LinearLayout.LayoutParams lp=new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
                            footerView.getMeasuredHeight());
                    Log.d(LOG_TAG,"addFooterView height="+footerView.getMeasuredHeight()
                        +" childcount="+this.getChildCount());
                    if(this.getChildCount()==2)
                    {
                        this.addView(footerView,lp);
                        footerView.setVisibility(View.VISIBLE);
                        invalidate();
                    }
                    Log.d(LOG_TAG,"childcount="+this.getChildCount());
                }else{
                    return false;
                }
            }
            break;
        }
        case MotionEventCompat.ACTION_POINTER_DOWN:{
            pointerIndex=MotionEventCompat.getActionIndex(event);
            if(pointerIndex<0){
                Log.e(LOG_TAG, "Got ACTION_POINTER_DOWN event but have an invalid action index.");
                return false;
            }
            mActivePointerId=MotionEventCompat.getPointerId(event,pointerIndex);
            break;
        }
        case MotionEvent.ACTION_POINTER_UP:
            onSecondaryPointerUp(event);
            break;
        case MotionEvent.ACTION_UP:{
            pointerIndex=MotionEventCompat.findPointerIndex(event,mActivePointerId);
            if(pointerIndex<0){
                Log.e(LOG_TAG, "Got ACTION_UP event but don't have an active pointer id.");
                return false;
            }
            final float y=MotionEventCompat.getY(event,pointerIndex);
            mIsBeingDragged=false;
            finishSpinner();
            mActivePointerId=INVALID_POINTER;
            return false;
        }
        case MotionEvent.ACTION_CANCEL:
            return false;
    }
    return true;
}

private void moveSpinner(float overscrollTop){
    float originalDragPercent=overscrollTop/mTotalDragDistance;
    float dragPercent=Math.min(1f,Math.abs(originalDragPercent));
    float adjustedPercent=(float)Math.max(dragPercent-.4,0)*5/3;
    float extraOS=Math.abs(overscrollTop)-mTotalDragDistance;
    float slingshotDist=mSpinnerFinalOffset;
    float tensionSlingshotPercent=Math.max(0,Math.min(extraOS,slingshotDist*2)/slingshotDist);
    float tensionPercent=(float)((tensionSlingshotPercent/4)-Math.pow(
            (tensionSlingshotPercent/4),2))*2f;
    float extraMove=(slingshotDist)*tensionPercent*2;

    int targetY=mOriginalOffsetTop+(int)((slingshotDist*dragPercent)+extraMove);
    setTargetOffsetTopAndBottom(targetY-mCurrentTargetOffsetTop,true);

}
private void finishSpinner(){
    if(currentHeaderHeight<0){
        setRefreshing(true,true);
    }else{
        //cancel refresh
        mRefreshing=false;
        animateOffsetToStartPosition();
    }
}
private void setRefreshing(boolean refreshing,final boolean notify)
{
    if(mRefreshing!=refreshing){
        ensureTarget();
        mRefreshing=refreshing;
        if(mRefreshing){
            refreshListener.onRefresh();
            arrowIv.setVisibility(View.GONE);
            arrowIv.clearAnimation();
            progressBar.setVisibility(View.VISIBLE);
        }else{
            arrowIv.setVisibility(View.GONE);
            progressBar.setVisibility(View.GONE);
            refreshTv.setText(getResources().getText(R.string.afterrefresh));
            animateOffsetToStartPosition();
        }
    }
}
public void setRefreshing(boolean refreshing){
    if(!refreshing){
        setRefreshing(refreshing,false);
    }
}
private void animateOffsetToStartPosition(){
    refreshTv.setText(getResources().getText(R.string.pulltorefresh));
    arrowIv.clearAnimation();
    if(timer==null&&currentHeaderHeight<headerHeight)
    {

        timer=new Timer();
        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                handler.post(new Runnable() {
                    @Override
                    public void run() {
                        if(currentHeaderHeight<headerHeight)
                        {
                            currentHeaderHeight+=5;
                            thisView.setY(-currentHeaderHeight);
                            mCurrentTargetOffsetTop = headerView.getTop();
                            if ( Build.VERSION.SDK_INT < 11) {
                                invalidate();
                            }
                        }else{
                            if(timer!=null)
                            {
                                arrowIv.setVisibility(View.VISIBLE);
                                progressBar.setVisibility(View.GONE);
                                timer.cancel();
                                timer=null;
                            }
                        }
                    }
                });
            }
        },10,10);
    }
}

/**
 * Classes that wish to be notified when the pull gesture correctly
 * triggers a refresh should implement this interface.
 */
public interface OnPullToRefresh{
    public void onRefresh();
}

/**
 * Classes that wish to be notified when the drag gesture correctly
 * triggers a load should implement this interface.
 */
public interface OnDragToLoad{
    public void onLoad();
}}

Upvotes: 2

Views: 2582

Answers (3)

Janki Gadhiya
Janki Gadhiya

Reputation: 4510

You need to implement View.OnTouchListener. Just modify your code like below :

public class RecyclerViewRefresh extends LinearLayout implements View.OnTouchListener
{
    @Override
    public boolean onTouchEvent(MotionEvent event)
    {
         ......
         measureView(footerView);
         footerView.setVisibility(View.VISIBLE);
         thisView.addView(footerView,lp);

         invalidate();
         .....
    }
 }

Try calling invalidate(); after addView();

Upvotes: 1

Mohit Charadva
Mohit Charadva

Reputation: 2593

Put your linear layout inside scroll view.

Upvotes: 2

Veselin Todorov
Veselin Todorov

Reputation: 301

If you're sure the view is added but it's not visible, make sure you have set the orientation of your LinearLayout. The default orientation is horizontal and maybe the view is added to the side of your existing views and is not visible. You can set it to vertical orientation by using:

myLayout.setOrientation(LinearLayout.VERTICAL);

Upvotes: 3

Related Questions