我已经设法让我的应用中的片段按照我的要求正确移动,但是,有一些与动画不一致的东西。
更具体一点:我有一个2帧的活动和3个片段。两个是静止的,第三个是从左到右移动,覆盖并揭开前两个。
当移动片段移动时,它会在从开始到结束之前暂时显示在其最终位置(使用动画资源slide_in_left,slide_in_right,slide_out_left和slide_out_right)。
编辑:我的测试设备是第二代nexus 7和三星galaxy s3,因此它不应该是硬件问题。模拟器跳过太多帧以注意它。如果我将所有3个片段一起移动,则不会发生这种情况。
我已经做了一个简单的应用程序来演示这个问题,也许有人可以对解决方案有所了解。以下是相关文件:
MainActivity.java
public class MainActivity extends Activity {
MainFragment mFragment1;
MainFragment mFragment2;
MainFragment mFragment3;
// variable to hold current frame for mFragment2
int mFrag2Frame;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if (savedInstanceState != null) {
mFrag2Frame = savedInstanceState.getInt("frag_2_frame");
} else {
mFrag2Frame = R.id.rightFrame;
}
// First add mFragment1 and mFragment3 where they will stay.
// Then add mFragment2 over the top of its current frame
FragmentTransaction ft = getFragmentManager().beginTransaction();
ft.add(R.id.leftFrame, mFragment1 = MainFragment.Frag1())
.add(R.id.rightFrame, mFragment3 = MainFragment.Frag3())
.add(mFrag2Frame, mFragment2 = MainFragment.Frag2())
.commit();
}
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putInt("frag_2_frame", mFrag2Frame);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
int id = item.getItemId();
if (id == R.id.action_slide) {
slide();
return true;
}
return super.onOptionsItemSelected(item);
}
public void slide() {
boolean isLeft = mFrag2Frame == R.id.leftFrame;
FragmentTransaction ft = getFragmentManager().beginTransaction();
// set animations according to current position of mFragment2
ft.setCustomAnimations(isLeft ? R.anim.slide_in_left : R.anim.slide_in_right,
isLeft ? R.anim.slide_out_right : R.anim.slide_out_left);
// change mFrag2Frame according to its current value
mFrag2Frame = isLeft ? R.id.rightFrame : R.id.leftFrame;
ft.remove(mFragment2)
.add(mFrag2Frame, mFragment2 = MainFragment.Frag2())
.commit();
}
}
main.xml中
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/LinearLayout1"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context="com.example.slidingpanels.MainActivity" >
<FrameLayout
android:id="@+id/leftFrame"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1" >
</FrameLayout>
<FrameLayout
android:id="@+id/rightFrame"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1" >
</FrameLayout>
</LinearLayout>
MainFragment.java
public class MainFragment extends Fragment {
int mResourceId = R.layout.frag;
int mColour;
public MainFragment() {
}
private MainFragment(int colour) {
mColour = colour;
}
public static MainFragment Frag1() {
return new MainFragment(Color.RED);
}
public static MainFragment Frag2() {
return new MainFragment(Color.GREEN);
}
public static MainFragment Frag3() {
return new MainFragment(Color.BLUE);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(mResourceId, container, false);
view.setBackgroundColor(mColour);
return view;
}
}
frag.xml
<?xml version="1.0" encoding="utf-8"?>
<com.example.slidingpanels.SlidingImageView
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/SlidingImageView1"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:src="@drawable/ic_launcher" >
</com.example.slidingpanels.SlidingImageView>
SlidingImageView.java
public class SlidingImageView extends ImageView {
public SlidingImageView(Context context, AttributeSet attrs) {
super(context, attrs);
// TODO Auto-generated constructor stub
}
public float getXFraction() {
int width = this.getWidth();
return (width == 0) ? 0 : getX() / (float) width;
}
public void setXFraction(float xFraction) {
int width = this.getWidth();
setX((width > 0) ? (xFraction * width) : 0);
}
}
最后,我将包含我的slide_in_left.xml
<?xml version="1.0" encoding="utf-8"?>
<set>
<objectAnimator
xmlns:android="http://schemas.android.com/apk/res/android"
android:propertyName="xFraction"
android:valueType="floatType"
android:valueFrom="-1"
android:valueTo="0"
android:duration="500" />
</set>
答案 0 :(得分:2)
我有完全相同的问题...
引起闪烁/故障是因为首次调用setXFraction之前它已经发生了onMeasure,所以getWidth()返回0意味着第一个绘制的帧片段处于非滚动位置,下一个调用发生在onMeasure之后并且它在正确的位置绘制,因为getWidth现在返回有效数据。
我发现的解决方案是将视图对象中的X分数保存为float,然后覆盖onMeasure()并使用mXfract * measureSpec重新执行setTranslationX。
适用于我,下面的代码段:
public class SlidingFrameLayout extends FrameLayout {
private static final String TAG = SlidingFrameLayout.class.getSimpleName();
private float mXfract=0f;
private float mYfract=0f;
public SlidingFrameLayout(Context context) {
super(context);
}
public SlidingFrameLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}
public SlidingFrameLayout(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
public void setYFraction(final float fraction) {
mYfract=fraction;
float translationY = getHeight() * fraction;
setTranslationY(translationY);
}
public float getYFraction() {
return mYfract;
}
public void setXFraction(final float fraction) {
mXfract=fraction;
float translationX = getWidth() * fraction;
setTranslationX(translationX);
}
public float getXFraction() {
return mXfract;
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int width = MeasureSpec.getSize(widthMeasureSpec);
int height = MeasureSpec.getSize(heightMeasureSpec);
// Correct any translations set before the measure was set
setTranslationX(mXfract*width);
setTranslationY(mYfract*height);
}
}
编辑:使用measurepec作为宽度/高度而不是调用。