草庐IT

android - 如何像垂直 Viewpager 变换一样为 RecyclerView 设置动画

coder 2023-12-05 原文

我在我的项目中使用了Vertical Viewpager

但也有一些问题,

  1. 当页面有很多 onclick() 事件时,滚动太费力

  2. fling 事件不会改变页面

  3. 我尝试使用手势检测器,但它在没有转换的情况下更改页面的速度太快(没有调用 transformPage())

  4. 当我滚动页面时,有时也会触发 onclick() 事件

所以我决定在 PagerSnapHelper 的帮助下将 Recyclerview 用作 Viewpager

它工作正常。但问题是,

how to do the transition or animation when the item is changed (like I did in ViewPager)

例如,Viewpager 中的 Zoomout 过渡或堆栈过渡。

我试过了 stackLayoutManager但滚动并尝试此操作需要更多时间 related link .它不起作用。

我研究了如何降低 viewpager 的滚动速度如何在 recyclerview 中制作动画。我没有得到解决方案。

谁能帮帮我!!!是否可能,或者我需要使用任何其他小部件。

编辑

我尝试了#ADM 的建议,效果很好,但是 doesn't support PagerSnapHelper.

我在上面的链接中更改了堆栈布局管理器,但它不支持 scrollToPosition() 和 PagerSnapHelper。

代码:

public class StackLayoutManager  extends LinearLayoutManager {

    private static final String TAG = "StackLayoutManager";

    //the space unit for the stacked item
    private int mSpace = 0;
    /**
     * the offset unit,deciding current position(the sum of  {@link #mItemWidth} and {@link #mSpace})
     */
    private int mUnit;
    //item width
    private int mItemWidth;
    private int mItemHeight;
    //the counting variable ,record the total offset including parallex
    private int mTotalOffset;
    //record the total offset without parallex
    private int mRealOffset;
    private ObjectAnimator animator;
    private int animateValue;
    private RecyclerView.Recycler recycler;
    private int lastAnimateValue;
    private int initialOffset;
    private boolean initial;
    private int mMinVelocityX;
    private VelocityTracker mVelocityTracker = VelocityTracker.obtain();
    private int pointerId;
    private Method sSetScrollState;
    RecyclerView recyclerView;


    public StackLayoutManager(Context context) {
        super(context, VERTICAL, false);
        setAutoMeasureEnabled(true);

    }

    @Override
    public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
        this.recycler = recycler;
        detachAndScrapAttachedViews(recycler);
        //got the mUnit basing on the first child,of course we assume that  all the item has the same size
        View anchorView = recycler.getViewForPosition(0);
        measureChildWithMargins(anchorView, View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED);
        mItemWidth = anchorView.getMeasuredWidth();
        mItemHeight = getDecoratedMeasuredHeight(anchorView);

        mUnit = mItemHeight;
        //because this method will be called twice
        initialOffset = mUnit;
        mMinVelocityX = ViewConfiguration.get(anchorView.getContext()).getScaledMinimumFlingVelocity();
        fill(recycler, 0);

    }


    @Override
    public void onLayoutCompleted(RecyclerView.State state) {
        super.onLayoutCompleted(state);
        if (!initial) {
            fill(recycler, initialOffset, false);
            initial = true;
        }
    }

    @Override
    public void onAdapterChanged(RecyclerView.Adapter oldAdapter, RecyclerView.Adapter newAdapter) {
        initial = false;
        mTotalOffset = mRealOffset = 0;
    }


    private int fill(RecyclerView.Recycler recycler, int dy, boolean apply) {
        return fillFromTop(recycler, dy);
    }

    public int fill(RecyclerView.Recycler recycler, int dy) {
        return fill(recycler, dy, true);
    }

    private int fillFromTop(RecyclerView.Recycler recycler, int dy) {
        if (mTotalOffset + dy < 0 || (mTotalOffset + dy + 0f) / mUnit > getItemCount() - 1)
            return 0;
        detachAndScrapAttachedViews(recycler);
        mTotalOffset += dy;
        int count = getChildCount();
        for (int i = 0; i < count; i++) {
            View child = getChildAt(i);
            if (recycleVertically(child, dy))
                removeAndRecycleView(child, recycler);
        }
        int curPos = mTotalOffset / mUnit;
        int leavingSpace = getHeight() - (left(curPos) + mUnit);
        int itemCountAfterBaseItem = leavingSpace / mUnit + 2;
        int e = curPos + itemCountAfterBaseItem;

        int start = curPos - 1 >= 0 ? curPos - 1 : 0;
        int end = e >= getItemCount() ? getItemCount() - 1 : e;

        int left = getWidth() / 2 - mItemWidth / 2;
        //layout views
        for (int i = start; i <= end; i++) {
            View view = recycler.getViewForPosition(i);

            float alpha = alpha(i);

            addView(view);
            measureChildWithMargins(view, 0, 0);
            int top =  (left(i) /* - ( 1 - scale ) * view.getMeasuredHeight() */ );
            int right = view.getMeasuredWidth() + left;
            int bottom = view.getMeasuredHeight() + top;
            layoutDecoratedWithMargins(view, left, top, right, bottom);
            view.setAlpha(alpha);
            view.setScaleY(1);
            view.setScaleX(1);
        }

        return dy;
    }


    private View.OnTouchListener mTouchListener = new View.OnTouchListener() {
        @Override
        public boolean onTouch(View v, MotionEvent event) {

            mVelocityTracker.addMovement(event);
            if (event.getAction() == MotionEvent.ACTION_DOWN) {
                if (animator != null && animator.isRunning())
                    animator.cancel();
                pointerId = event.getPointerId(0);

            }
            if (event.getAction() == MotionEvent.ACTION_UP) {
                if (v.isPressed())
                    v.performClick();
                mVelocityTracker.computeCurrentVelocity(1000, 14000);
                float xVelocity = mVelocityTracker.getYVelocity(pointerId);
                int o = mTotalOffset % mUnit;
                int scrollX;
                if (Math.abs(xVelocity) < mMinVelocityX && o != 0) {
                    if (o >= mUnit / 2)
                        scrollX = mUnit - o;
                    else
                        scrollX = -o;
                    Log.d("scrollx","from scroll");

                    Log.d("scrollx", "" + scrollX);
                    brewAndStartAnimator(300, (int) (scrollX));
                }
            }
            return false;
        }

    };

    private RecyclerView.OnFlingListener mOnFlingListener = new RecyclerView.OnFlingListener() {
        @Override
        public boolean onFling(int velocityX, int velocityY) {
            int o = mTotalOffset % mUnit;
            int s = mUnit - o;
            int scrollX;
            int vel = absMax(velocityX, velocityY);
            if (vel  > 0) {
                scrollX = s;
            } else
                scrollX = -o;
            Log.d("scrollx","from fling");
            Log.d("scrollx",""+scrollX);
            brewAndStartAnimator(100, scrollX);
          //  setScrollStateIdle();
            return true;
        }
    };

    private int absMax(int a, int b) {
        if (Math.abs(a) > Math.abs(b))
            return a;
        else return b;
    }

    @Override
    public void onAttachedToWindow(RecyclerView view) {
        super.onAttachedToWindow(view);
        this.recyclerView=view;
        view.setOnTouchListener(mTouchListener);
        view.setOnFlingListener(mOnFlingListener);
    }


    private void brewAndStartAnimator(int dur, int finalX) {
        animator = ObjectAnimator.ofInt(StackLayoutManager.this, "animateValue", 0, finalX);
        animator.setDuration(dur);
        animator.start();
        animator.addListener(new AnimatorListenerAdapter() {
            @Override
            public void onAnimationEnd(Animator animation) {
                lastAnimateValue = 0;
                recyclerView.scrollToPosition(findFirstCompletelyVisibleItemPosition());

            }

            @Override
            public void onAnimationCancel(Animator animation) {
                lastAnimateValue = 0;
                recyclerView.smoothScrollToPosition(findFirstCompletelyVisibleItemPosition());
            }
        });

    }

    private float alpha(int position) {
        float alpha;
        int curPos = mTotalOffset / mUnit;
        float n = (mTotalOffset + .0f) / mUnit;
        if (position > curPos)
            alpha = 1.0f;
        else {
            alpha = 1 - (n - position) / 1;
        }
        return alpha <= 0.001f ? 0 : alpha;
    }

    @Override
    public void scrollToPosition(int pos)
    {
          scrollToPositionWithOffset(pos, 0);
    }

    private int left(int position) {


        int curPos = mTotalOffset / mUnit;
        int tail = mTotalOffset % mUnit;
        float n = (mTotalOffset + .0f) / mUnit;
        float x = n - curPos;

        return ltr(position, curPos, tail, x);

    }

    private int ltr(int position, int curPos, int tail, float x) {
        int left;
        if (position <= curPos) {
            if (position == curPos) {
                left = (int) (mSpace * (1 - x));
            } else {
                left = (int) (mSpace * (1 - x - (curPos - position)));

            }
        } else {
            if (position == curPos + 1)
                left = mSpace + mUnit - tail;
            else {

                int baseStart = (int) (mUnit +  mUnit);
                left = (int) (baseStart + (position - curPos - 2) * mUnit - (position - curPos - 2) * (mUnit));
                if (BuildConfig.DEBUG)
                    Log.i(TAG, "ltr: curPos " + curPos + "  pos:" + position + "  left:" + left + "   baseStart" + baseStart + " curPos+1:" + left(curPos + 1));
            }
            left = left <= 0 ? 0 : left;
        }
        return left;
    }


    @SuppressWarnings("unused")
    public void setAnimateValue(int animateValue) {
        this.animateValue = animateValue;
        int dy = this.animateValue - lastAnimateValue;
        fill(recycler, dy, false);
        lastAnimateValue = animateValue;
    }

    @SuppressWarnings("unused")
    public int getAnimateValue() {
        return animateValue;
    }


    private boolean recycleVertically(View view, int dy) {
        return view != null && (view.getTop() - dy < 0 || view.getBottom() - dy > getHeight());
    }


    @Override
    public int scrollVerticallyBy(int dy, RecyclerView.Recycler recycler, RecyclerView.State state) {
        return fill(recycler, dy);
    }

    @Override
    public boolean canScrollHorizontally() {
        return false;
    }

    @Override
    public boolean canScrollVertically() {
        return true;
    }

    @Override
    public RecyclerView.LayoutParams generateDefaultLayoutParams() {
        return new RecyclerView.LayoutParams(RecyclerView.LayoutParams.MATCH_PARENT, RecyclerView.LayoutParams.MATCH_PARENT);
    }

    @SuppressWarnings("unused")
    public interface CallBack {

        float scale(int totalOffset, int position);

        float alpha(int totalOffset, int position);

        float left(int totalOffset, int position);
    }
}

如果 recyclerview 无法实现,请帮助我如何处理 VerticalViewPager。我试过设置字段值,但它不起作用。

    try {
        Class cls = this.getClass().getSuperclass();
        Field distanceField = cls.getDeclaredField("mFlingDistance");
        distanceField.setAccessible(true);
        distanceField.setInt(this, distanceField.getInt(this)/2);
    }catch (Exception ignored) {
        Log.d("error", ignored.toString());
    }


    try {
        Class cls = this.getClass().getSuperclass();
        Field minVelocityField = cls.getDeclaredField("mMinimumVelocity");
        minVelocityField.setAccessible(true);
        minVelocityField.setInt(this, minVelocityField.getInt(this) /2);
    } catch (Exception ignored) {
        Log.d("error", ignored.toString());
    }


    try {
        Class cls = this.getClass().getSuperclass();
        Field maxVelocityField = cls.getDeclaredField("mMaximumVelocity");
        maxVelocityField.setAccessible(true);
        maxVelocityField.setInt(this, maxVelocityField.getInt(this)/2);
    }catch (Exception ignored) {
        Log.d("error", ignored.toString());
    }

最佳答案

试试这个

import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import java.util.ArrayList;
import android.support.animation.DynamicAnimation;
import android.support.animation.FlingAnimation;


public class MainActivity extends AppCompatActivity {


    CustomVerticalViewPager viewPager;
    ArrayList<DataModel> arrayList = new ArrayList<>();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        initArray();

        viewPager = findViewById(R.id.viewPager);

        viewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
        @Override
        public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
            FlingAnimation fling = new FlingAnimation(viewPager, DynamicAnimation.SCROLL_X);
        }

        @Override
        public void onPageSelected(int position) {

        }

        @Override
        public void onPageScrollStateChanged(int state) {

        }
    });


        CustomAdapter adapter = new CustomAdapter(this, arrayList);
        viewPager.setAdapter(adapter);
    }

    private void initArray() {
        for (int i = 0; i < 10; i++) {
            DataModel dataModel = new DataModel();
            dataModel.setTitle("Title item No :- " + i);
            dataModel.setContent(" Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras auctor blandit dignissim. Suspendisse id lorem nulla. Proin urna lacus, posuere sed lacus a, dapibus consectetur neque. Donec in metus sagittis, consequat tellus eget, consequat ex. Interdum et malesuada fames ac ante ipsum primis in faucibus. Donec dui nisi, scelerisque eu nunc id, aliquet sagittis leo. Nunc ac ornare diam. Vestibulum sed elit euismod, ornare metus a, convallis ipsum. Nulla aliquam mi enim, porttitor commodo lacus dictum cursus. Phasellus sed eros sagittis, feugiat sapien ac, accumsan odio. In posuere congue lorem, quis pharetra mi tincidunt et. Nam tincidunt erat eu dapibus faucibus.\n" +
                    "\n");

            arrayList.add(dataModel);
        }

    }


}

LAYOUT

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <com.example.nilesh.testapp.CustomVerticalViewPager
        android:layout_width="match_parent"
        android:id="@+id/viewPager"
        android:layout_height="match_parent" />


</LinearLayout>

CustomVerticalViewPager

import android.content.Context;
import android.support.v4.view.ViewPager;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;

public class CustomVerticalViewPager extends ViewPager {
    private GestureDetector gestureDetector;
    public boolean isScrollEvent;
    public CustomVerticalViewPager(Context context) {
        super(context);
        init();
    }

    public CustomVerticalViewPager(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    private void init() {
        gestureDetector=new GestureDetector(getContext(),new GestureListener());
        setPageTransformer(true, new VerticalPageTransformer());
        setOverScrollMode(OVER_SCROLL_NEVER);
        try {
            Field mScroller;
            mScroller = ViewPager.class.getDeclaredField("mScroller");
            mScroller.setAccessible(true);
            FixedSpeedScroller scroller = new FixedSpeedScroller(getContext(), new DecelerateInterpolator());
            scroller.setScrollDuration(250);
            mScroller.set(this, scroller);
        } catch (Exception e) {}
    }


    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        return gestureDetector.onTouchEvent((ev));

    }

    @Override
    public boolean onTouchEvent(MotionEvent ev) {
        boolean res= gestureDetector.onTouchEvent((ev));
        if((ev.getAction() == MotionEvent.ACTION_CANCEL ||ev.getAction()==MotionEvent.ACTION_UP)) {
            if(isScrollEvent) {
                try {
                    endFakeDrag();
                } catch (Exception e) {}
            }
            return true;
        }
       // Log.d("event", "ontouch      " + res);
        return res;
    }

    private class FixedSpeedScroller extends Scroller {

        private int mDuration = 500;

        public FixedSpeedScroller(Context context) {
            super(context);
        }

        public FixedSpeedScroller(Context context, Interpolator interpolator) {
            super(context, interpolator);
        }

        public FixedSpeedScroller(Context context, Interpolator interpolator, boolean flywheel) {
            super(context, interpolator, flywheel);
        }

        public void setScrollDuration(int duration) {
            mDuration = duration;
        }

        @Override
        public void startScroll(int startX, int startY, int dx, int dy, int duration) {
            super.startScroll(startX, startY, dx, dy, mDuration);
        }

        @Override
        public void startScroll(int startX, int startY, int dx, int dy) {
            super.startScroll(startX, startY, dx, dy, mDuration);
        }
    }

    private final class GestureListener extends GestureDetector.SimpleOnGestureListener {
        @Override
        public boolean onSingleTapUp(MotionEvent e) {
            isScrollEvent=false;
          // Log.d("touch","singletap");
            return false;
        }
        @Override
        public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
        //   Log.d("touch","fling");
            isScrollEvent=false;
            try {
                float diffY = e2.getY() - e1.getY();
                if (diffY > 20) {
                    onSwipeDown();
                    return  false;
                } else if(diffY<-20){
                    onSwipeUp();
                    return  false;
                }
                else
                {
                    endFakeDrag();
                }


            } catch (Exception exception) {
                exception.printStackTrace();
            }
            return false;

        }
        @Override
        public boolean onScroll(MotionEvent e1, MotionEvent e2,
                                float distanceX, float distanceY) {
        // Log.d("touch", "scroll" + " " + distanceX);
            beginFakeDrag();
            fakeDragBy(-distanceY);
            isScrollEvent=true;
            return true;

        }
    }
    public void onSwipeUp() {
        setCurrentItem(getCurrentItem() + 1);
    }
    public void onSwipeDown() {
        setCurrentItem(getCurrentItem() - 1);

    }
}

CustomAdapter

import android.content.Context;
import android.support.v4.view.PagerAdapter;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.LinearLayout;
import android.widget.TextView;
import android.widget.Toast;
import java.util.ArrayList;

public class CustomAdapter extends PagerAdapter {
    Context context;
    ArrayList<DataModel> arrayList = new ArrayList<>();

    LayoutInflater mLayoutInflater;

    public CustomAdapter(Context context, ArrayList<DataModel> arrayList) {
        this.context = context;
        this.arrayList = arrayList;
        mLayoutInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    }


    @Override
    public int getCount() {
        return arrayList.size();
    }

    @Override
    public boolean isViewFromObject(View view, Object object) {
        return view == ((LinearLayout) object);
    }

    @Override
    public Object instantiateItem(ViewGroup container, final int position) {
        View itemView = mLayoutInflater.inflate(R.layout.custom_layout, container, false);

        TextView tvTitle = itemView.findViewById(R.id.tvTitle);
        TextView tvContent = itemView.findViewById(R.id.tvContent);

        tvContent.setText(arrayList.get(position).getContent());
        tvTitle.setText(arrayList.get(position).getTitle());


        tvTitle.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Toast.makeText(context, "clicked position :" + position + " " + arrayList.get(position).getTitle(), Toast.LENGTH_SHORT).show();
            }
        });


        container.addView(itemView);

        return itemView;
    }

    @Override
    public void destroyItem(ViewGroup container, int position, Object object) {
        container.removeView((LinearLayout) object);
    }
}

DataModel

public class DataModel {
    String title, content;

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public String getContent() {
        return content;
    }

    public void setContent(String content) {
        this.content = content;
    }
}

custom_layout

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">


    <TextView
        android:id="@+id/tvTitle"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:padding="5dp"
        android:text="Nilesh"
        android:textColor="#000"
        android:textSize="20sp"
        android:textStyle="bold" />

    <TextView
        android:id="@+id/tvContent"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />
</LinearLayout>

希望对你有帮助

编辑

对于 throw 效果使用 Move views using a fling animation

Fling-based animation uses a friction force that is proportional to an object's velocity. Use it to animate a property of an object and want to end the animation gradually. It has an initial momentum, which is mostly received from the gesture velocity, and gradually slows down. The animation comes to an end when the velocity of the animation is low enough that it makes no visible change on the device screen.

关于android - 如何像垂直 Viewpager 变换一样为 RecyclerView 设置动画,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50147206/

有关android - 如何像垂直 Viewpager 变换一样为 RecyclerView 设置动画的更多相关文章

  1. ruby - 如何使用 Nokogiri 的 xpath 和 at_xpath 方法 - 2

    我正在学习如何使用Nokogiri,根据这段代码我遇到了一些问题:require'rubygems'require'mechanize'post_agent=WWW::Mechanize.newpost_page=post_agent.get('http://www.vbulletin.org/forum/showthread.php?t=230708')puts"\nabsolutepathwithtbodygivesnil"putspost_page.parser.xpath('/html/body/div/div/div/div/div/table/tbody/tr/td/div

  2. ruby - 如何从 ruby​​ 中的字符串运行任意对象方法? - 2

    总的来说,我对ruby​​还比较陌生,我正在为我正在创建的对象编写一些rspec测试用例。许多测试用例都非常基础,我只是想确保正确填充和返回值。我想知道是否有办法使用循环结构来执行此操作。不必为我要测试的每个方法都设置一个assertEquals。例如:describeitem,"TestingtheItem"doit"willhaveanullvaluetostart"doitem=Item.new#HereIcoulddotheitem.name.shouldbe_nil#thenIcoulddoitem.category.shouldbe_nilendend但我想要一些方法来使用

  3. ruby - 使用 RubyZip 生成 ZIP 文件时设置压缩级别 - 2

    我有一个Ruby程序,它使用rubyzip压缩XML文件的目录树。gem。我的问题是文件开始变得很重,我想提高压缩级别,因为压缩时间不是问题。我在rubyzipdocumentation中找不到一种为创建的ZIP文件指定压缩级别的方法。有人知道如何更改此设置吗?是否有另一个允许指定压缩级别的Ruby库? 最佳答案 这是我通过查看ruby​​zip内部创建的代码。level=Zlib::BEST_COMPRESSIONZip::ZipOutputStream.open(zip_file)do|zip|Dir.glob("**/*")d

  4. python - 如何使用 Ruby 或 Python 创建一系列高音调和低音调的蜂鸣声? - 2

    关闭。这个问题是opinion-based.它目前不接受答案。想要改进这个问题?更新问题,以便editingthispost可以用事实和引用来回答它.关闭4年前。Improvethisquestion我想在固定时间创建一系列低音和高音调的哔哔声。例如:在150毫秒时发出高音调的蜂鸣声在151毫秒时发出低音调的蜂鸣声200毫秒时发出低音调的蜂鸣声250毫秒的高音调蜂鸣声有没有办法在Ruby或Python中做到这一点?我真的不在乎输出编码是什么(.wav、.mp3、.ogg等等),但我确实想创建一个输出文件。

  5. ruby-on-rails - 如何验证 update_all 是否实际在 Rails 中更新 - 2

    给定这段代码defcreate@upgrades=User.update_all(["role=?","upgraded"],:id=>params[:upgrade])redirect_toadmin_upgrades_path,:notice=>"Successfullyupgradeduser."end我如何在该操作中实际验证它们是否已保存或未重定向到适当的页面和消息? 最佳答案 在Rails3中,update_all不返回任何有意义的信息,除了已更新的记录数(这可能取决于您的DBMS是否返回该信息)。http://ar.ru

  6. ruby-openid:执行发现时未设置@socket - 2

    我在使用omniauth/openid时遇到了一些麻烦。在尝试进行身份验证时,我在日志中发现了这一点:OpenID::FetchingError:Errorfetchinghttps://www.google.com/accounts/o8/.well-known/host-meta?hd=profiles.google.com%2Fmy_username:undefinedmethod`io'fornil:NilClass重要的是undefinedmethodio'fornil:NilClass来自openid/fetchers.rb,在下面的代码片段中:moduleNetclass

  7. ruby-on-rails - 'compass watch' 是如何工作的/它是如何与 rails 一起使用的 - 2

    我在我的项目目录中完成了compasscreate.和compassinitrails。几个问题:我已将我的.sass文件放在public/stylesheets中。这是放置它们的正确位置吗?当我运行compasswatch时,它不会自动编译这些.sass文件。我必须手动指定文件:compasswatchpublic/stylesheets/myfile.sass等。如何让它自动运行?文件ie.css、print.css和screen.css已放在stylesheets/compiled。如何在编译后不让它们重新出现的情况下删除它们?我自己编译的.sass文件编译成compiled/t

  8. ruby - 如何将脚本文件的末尾读取为数据文件(Perl 或任何其他语言) - 2

    我正在寻找执行以下操作的正确语法(在Perl、Shell或Ruby中):#variabletoaccessthedatalinesappendedasafileEND_OF_SCRIPT_MARKERrawdatastartshereanditcontinues. 最佳答案 Perl用__DATA__做这个:#!/usr/bin/perlusestrict;usewarnings;while(){print;}__DATA__Texttoprintgoeshere 关于ruby-如何将脚

  9. ruby - 如何指定 Rack 处理程序 - 2

    Rackup通过Rack的默认处理程序成功运行任何Rack应用程序。例如:classRackAppdefcall(environment)['200',{'Content-Type'=>'text/html'},["Helloworld"]]endendrunRackApp.new但是当最后一行更改为使用Rack的内置CGI处理程序时,rackup给出“NoMethodErrorat/undefinedmethod`call'fornil:NilClass”:Rack::Handler::CGI.runRackApp.newRack的其他内置处理程序也提出了同样的反对意见。例如Rack

  10. ruby - 如何每月在 Heroku 运行一次 Scheduler 插件? - 2

    在选择我想要运行操作的频率时,唯一的选项是“每天”、“每小时”和“每10分钟”。谢谢!我想为我的Rails3.1应用程序运行调度程序。 最佳答案 这不是一个优雅的解决方案,但您可以安排它每天运行,并在实际开始工作之前检查日期是否为当月的第一天。 关于ruby-如何每月在Heroku运行一次Scheduler插件?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/questions/8692687/

随机推荐