在Android中同步两个水平滚动视图

5
我尝试遵循此帖子Synchronise ScrollView scroll positions - android中的建议,但遇到了问题。
背景 - 我想要一个表格布局,既有水平滚动,又有垂直滚动,但我希望第一行和第一列始终存在 - 就像在excel中冻结窗格一样。不幸的是,使用Android java编程似乎几乎不可能...这让我头疼不已。如果我可以同步这两个HorizontalScrollView,那么我将拥有我想要的表格工作方式。
我按照上面发布的链接的建议进行操作,我遇到的问题是这样的。 当我实现时,应用程序会强制关闭。

scrollView1.setScrollViewListener(this);

我认为问题可能在于我声明ObservableScrollViews的方式。我没有使用XML,所有对象都是通过编程创建的。我尝试使用

private ObservableScrollView oScrollViewOne = new ObservableScrollView (this);

但这也导致了强制关闭。(我可以只创建一个常规的ScrollView,分配一个ID,然后使用

scrollView1 = (ObservableScrollView) findViewById(ID); 其中ID是我给ScrollView分配的整数编号。

我想知道如何理解下面的注释,并且如果我不使用XML布局,该如何使用它们。
我们应该在布局中指定这个新的ObservableScrollView类,而不是现有的ScrollView标签。

com.test.ObservableScrollView android:id="@+id/scrollview1" ...

是否有其他建议来实现同步两个HorizontalScrollView,使用明确的代码 - 而不仅仅是建议使用OnTouchMotionEvent或其他没有给出代码的想法,正如我之前所看到的那样。
以下是错误代码。
10-23 23:33:08.631: ERROR/AndroidRuntime(18187): FATAL EXCEPTION: main
10-23 23:33:08.631: ERROR/AndroidRuntime(18187): java.lang.RuntimeException: Unable to instantiate activity ComponentInfo{com.glen.apps.TeacherAidePro/com.glen.apps.TeacherAidePro.TeacherAidePro}: java.lang.NullPointerException
10-23 23:33:08.631: ERROR/AndroidRuntime(18187):     at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2709)
10-23 23:33:08.631: ERROR/AndroidRuntime(18187):     at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2803)
10-23 23:33:08.631: ERROR/AndroidRuntime(18187):     at android.app.ActivityThread.access$2300(ActivityThread.java:135)
10-23 23:33:08.631: ERROR/AndroidRuntime(18187):     at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2136)
10-23 23:33:08.631: ERROR/AndroidRuntime(18187):     at android.os.Handler.dispatchMessage(Handler.java:99)
10-23 23:33:08.631: ERROR/AndroidRuntime(18187):     at android.os.Looper.loop(Looper.java:144)
10-23 23:33:08.631: ERROR/AndroidRuntime(18187):     at android.app.ActivityThread.main(ActivityThread.java:4937)
10-23 23:33:08.631: ERROR/AndroidRuntime(18187):     at java.lang.reflect.Method.invokeNative(Native Method)
10-23 23:33:08.631: ERROR/AndroidRuntime(18187):     at java.lang.reflect.Method.invoke(Method.java:521)
10-23 23:33:08.631: ERROR/AndroidRuntime(18187):     at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:868)
10-23 23:33:08.631: ERROR/AndroidRuntime(18187):     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:626)
10-23 23:33:08.631: ERROR/AndroidRuntime(18187):     at dalvik.system.NativeStart.main(Native Method)
10-23 23:33:08.631: ERROR/AndroidRuntime(18187): Caused by: java.lang.NullPointerException
10-23 23:33:08.631: ERROR/AndroidRuntime(18187):     at android.content.ContextWrapper.getResources(ContextWrapper.java:80)
10-23 23:33:08.631: ERROR/AndroidRuntime(18187):     at android.view.View.<init>(View.java:1810)
10-23 23:33:08.631: ERROR/AndroidRuntime(18187):     at android.view.View.<init>(View.java:1856)
10-23 23:33:08.631: ERROR/AndroidRuntime(18187):     at android.view.ViewGroup.<init>(ViewGroup.java:299)
10-23 23:33:08.631: ERROR/AndroidRuntime(18187):     at android.widget.FrameLayout.<init>(FrameLayout.java:83)
10-23 23:33:08.631: ERROR/AndroidRuntime(18187):     at android.widget.ScrollView.<init>(ScrollView.java:137)
10-23 23:33:08.631: ERROR/AndroidRuntime(18187):     at android.widget.ScrollView.<init>(ScrollView.java:133)
10-23 23:33:08.631: ERROR/AndroidRuntime(18187):     at android.widget.ScrollView.<init>(ScrollView.java:129)
10-23 23:33:08.631: ERROR/AndroidRuntime(18187):     at com.glen.apps.TeacherAidePro.ObservableScrollView.<init>(ObservableScrollView.java:12)
10-23 23:33:08.631: ERROR/AndroidRuntime(18187):     at com.glen.apps.TeacherAidePro.TeacherAidePro.<init>(TeacherAidePro.java:119)
10-23 23:33:08.631: ERROR/AndroidRuntime(18187):     at java.lang.Class.newInstanceImpl(Native Method)
10-23 23:33:08.631: ERROR/AndroidRuntime(18187):     at java.lang.Class.newInstance(Class.java:1429)
10-23 23:33:08.631: ERROR/AndroidRuntime(18187):     at android.app.Instrumentation.newActivity(Instrumentation.java:1036)
10-23 23:33:08.631: ERROR/AndroidRuntime(18187):     at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2701)
10-23 23:33:08.631: ERROR/AndroidRuntime(18187):     ... 11 more

这里是第12行

 1. package com.glen.apps.TeacherAidePro;
 2.
 3. import android.content.Context;
 4. import android.util.AttributeSet;
 5. import android.widget.ScrollView;
 6.
 7. public class ObservableScrollView extends ScrollView {
 8.
 9.   private IScrollListener listener = null;    
10.
11.   public ObservableScrollView(Context context) {
12.       super(context);
  }

你能把logcat中的异常信息发出来吗?这样可以更准确地找到问题所在。 - skynet
发布在原始帖子中。谢谢。 - gbotha
2个回答

6
错误指向您的ObservableScrollView构造函数中的空指针。请提供构造函数以及其第12行?
我看到您的XML和自定义滚动视图交互的一种可能的问题是,您的自定义滚动视图是一个内部类。请参见this page以了解如何在XML中声明内部类自定义组件,或将其移动到外部类。我通常将其放在外部类中,但如果您想将其保留为内部类,则应该像这样:
<view
    class="com.glen.apps.TeacherAidePro$ObservableScrollView"
.../>

我认为这并没有解释你的空指针异常,因此请发布你的ObservableScrollView类。
编辑:
如果你坚持用Java做所有事情,这里有一个可行的例子:
private ObservableScrollView scrollView1 = null;
private ObservableScrollView scrollView2 = null;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    LinearLayout parent = new LinearLayout(this);
    parent.setOrientation(LinearLayout.HORIZONTAL);
    parent.setLayoutParams(new LayoutParams(LayoutParams.FILL_PARENT,
            LayoutParams.FILL_PARENT));
    parent.setWeightSum(2.0f);

    scrollView1 = new ObservableScrollView(this);
    scrollView1.setLayoutParams(new LayoutParams(LayoutParams.FILL_PARENT,
            LayoutParams.FILL_PARENT, 1.0f));
    scrollView2 = new ObservableScrollView(this);
    scrollView2.setLayoutParams(new LayoutParams(LayoutParams.FILL_PARENT,
            LayoutParams.FILL_PARENT, 1.0f));

    scrollView1.setScrollViewListener(new ScrollViewListener() {
        public void onScrollChanged(ObservableScrollView scrollView, int x,
                int y, int oldx, int oldy) {
            scrollView2.scrollTo(x, y);
        }
    });
    scrollView2.setScrollViewListener(new ScrollViewListener() {
        public void onScrollChanged(ObservableScrollView scrollView, int x,
                int y, int oldx, int oldy) {
            scrollView1.scrollTo(x, y);
        }
    });

    TextView tv1 = new TextView(this);
    tv1.setText("TEXT1TEXTTEXTTEXTTEXTTEXTTEXTTEXTTEXTTEXTTEXTTEXTTEXTTEXTTEXTTEXTTEXTTEXTTEXTTEXTTEXTTEXTTEXTTEXTTEXTTEXTTEXTTEXTTEXTTEXTTEXTTEXTTEXTTEXTTEXTTEXTTEXTTEXTTEXTTEXTTEXTTEXTTEXTTEXTTEXT");
    tv1.setTextSize(36.0f);
    scrollView1.addView(tv1);

    TextView tv2 = new TextView(this);
    tv2.setText("TEXT2TEXTTEXTTEXTTEXTTEXTTEXTTEXTTEXTTEXTTEXTTEXTTEXTTEXTTEXTTEXTTEXTTEXTTEXTTEXTTEXTTEXTTEXTTEXTTEXTTEXTTEXTTEXTTEXTTEXTTEXTTEXTTEXTTEXTTEXTTEXTTEXTTEXTTEXTTEXTTEXTTEXTTEXTTEXTTEXT");
    tv2.setTextSize(36.0f);
    scrollView2.addView(tv2);

    parent.addView(scrollView1);
    parent.addView(scrollView2);
    parent.invalidate();

    setContentView(parent);
}

基本上,这将创建两个并排的滚动视图,每个权重为1.0,并将它们放在LinearLayout中,该布局的总权重为2.0,因此它们各自获得一半的宽度。

然而,我强烈建议您熟悉XML,因为它更加容易(在我看来)创建布局。找出错误也更容易,XML的嵌套形式使其更易于阅读。无论如何,希望这能澄清事情。


谢谢。我会在今天下午发布它。一个要点是我根本不使用XML——我在实际的主活动代码中声明所有内容。然而,关于如何同步两个水平滚动条的原始帖子涉及使用XML布局,而我正试图避免这种情况(并且对XML代码的解释也不足够)。 - gbotha
我已经提供了一个Java的工作示例,展示如何实现它。不过我建议你熟悉XML,因为一旦你习惯了它,就会变得非常简单明了! - skynet
1
我不使用XML的原因是当用户点击各种按钮时,应用程序会动态地向布局添加行/列。据我所见,使用Java代码实现这一点要容易得多。 - gbotha
如果你的ObservableScrollView与你链接的示例中的相同,那么我认为它不是问题所在。请发布在声明和初始化视图的活动中的onCreate方法。另外,请参考我的示例,了解如何实现同步滚动视图。 - skynet
-Craigy,我也在使用这段代码,但它抛出了空指针异常,你能帮我吗?- Hemant - hemant
显示剩余3条评论

1
这是我的解决方案,在代码中进一步说明:
//This should be inside yout oncreate
syncScrolls(firstView, secondView;
        syncScrolls(secondView, firstView);




    //This method accept 2 horizontal scroll views, but you could improve it passing an array. 
        private void syncScrolls(final HorizontalScrollView currentView, final HorizontalScrollView otherView) {

//This create the Gesture Listener where we override the methods we need
                GestureDetector.SimpleOnGestureListener gestureListener = new GestureDetector.SimpleOnGestureListener() {
                    @Override
                    public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
//Here onfling just return true, this way doesnt happens
                        return true;
                    }
                    @Override
                    public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
//On scroll we sync the movement of the both views
                        otherView.scrollTo(currentView.getScrollX(), 0);
                        return super.onScroll(e1, e2, distanceX, distanceY);
                    }
                    @Override
                    public boolean onDown(MotionEvent e) {
                        return true;
                    }
                };
        //This create the gesture detectors that implements the previous custom gesture listener
                final GestureDetector gestureDetector = new GestureDetector(this, gestureListener);
        //And finally we set everything to the views we need
                currentView.setOnTouchListener(new View.OnTouchListener() {
                    public boolean onTouch(View v, MotionEvent event) {
                        return gestureDetector.onTouchEvent(event);
                    }
                });

        }

我不确定这是完全正确的答案,正如我之前提到的,您可以通过使用数组来改进它,而不是为每个视图调用同步方法,在这种情况下只需调用2次。此外,如果您的滚动视图过长,则会阻止onFling而不是同步,这可能不太用户友好。最后,如果用户进行微小的快速滑动,则仍存在一些小问题,但这是非常罕见的情况。


我决定尝试在OnTouch中同步滚动视图 - 只需移动otherView.scrollTo(currentView.getScrollX(), 0),它就可以很好地工作。我根本不需要onScroll方法。 - Larry

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接