自定义圆形视图。放置视图

2024-04-24

昨天我尝试创建一个自定义视图,其中所有元素 像按钮,图像按钮,...被放置在一个圆圈周围 间距相等。

我的目标是得到这个:

我尝试了这段代码:

public class CircleView extends RelativeLayout {
    private OnDrop onDrop = null;
    private int radius = -1;
    private double step = -1;
    private double angle = -1;
    private static final int CENTER_ID = 111;

    public CircleView(Context context, OnDrop onDrop, int radius, List<View> elements) {
        super(context);

        this.onDrop = onDrop;
        this.radius = radius;
        this.step = (2 * Math.PI) / elements.size();

        RelativeLayout.LayoutParams layoutParamsView = new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.MATCH_PARENT, RelativeLayout.LayoutParams.MATCH_PARENT);

        this.setLayoutParams(layoutParamsView);
        this.addView(this.createCenter(context));

        for(View view : elements) {
            this.addView(this.placeView(view));
        }
    }

    private View placeView(View view) {
        RelativeLayout.LayoutParams layoutParams = createNewRelativeLayoutParams();

        int x = (int)(view.getWidth() / 2 + this.radius * Math.cos(this.angle));
        int y = (int)(view.getHeight() / 2 + this.radius * Math.sin(this.angle));

        this.angle += this.step;

        layoutParams.setMargins(x, 0, 0, y);
        view.setLayoutParams(layoutParams);

        return view;
    }

    private View createCenter(Context context) {
        RelativeLayout.LayoutParams layoutParams = new RelativeLayout.LayoutParams(Globals.DRAG_TARGET_SIZE, Globals.DRAG_TARGET_SIZE);
        layoutParams.addRule(CENTER_HORIZONTAL);
        layoutParams.addRule(CENTER_VERTICAL);

        Button button = new Button(context);
        button.setId(CENTER_ID);
        button.setLayoutParams(layoutParams);

        return button;
    }

    private RelativeLayout.LayoutParams createNewRelativeLayoutParams() {
        RelativeLayout.LayoutParams layoutParameters = new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.WRAP_CONTENT, RelativeLayout.LayoutParams.WRAP_CONTENT);
        layoutParameters.addRule(RelativeLayout.ABOVE, CENTER_ID);
        layoutParameters.addRule(RIGHT_OF, CENTER_ID);

        return layoutParameters;
    }
}

我从这段代码中得到的是这样的:

这意味着创建一个间距相等的圆 这些元素运行得很好。但还有一个问题 随着放置。 x 和 y 坐标有问题。

有人可以帮我弄清楚如何纠正这个偏移吗?


我实现了自己的布局,将按钮围绕中心点循环放置:

/**
 *
 */
public class MenuCircularLayout extends MenuDragableButtonsLayout {
    /**
     * 
     */
    private RelativeLayout relativeLayoutCenter = null;
    /**
     * 
     */
    private List<View> menuButtons = null;
    /**
     * 
     */
    private ImageButton imageButtonCenter = null;
    /**
     * 
     */
    private int radius = -1;
    /**
     * 
     */
    private double step = -1;
    /**
     * 
     */
    private double angle = -1;
    /**
     * 
     */
    private static final int CENTER_ID = View.generateViewId();

    /**
     * 
     * @param context
     * @param callbackDrag
     * @param menuButtons
     */
    public MenuCircularLayout(Context context, CallbackDrag callbackDrag, List<View> menuButtons) {
        super(context);

        this.callbackDrag = callbackDrag;
        this.menuButtons = menuButtons;
        this.radius = Sizes.getDpToPx(context, Sizes.MENU_ICON_RADIUS);
        this.step = (2 * Math.PI) / menuButtons.size();

        this.initView();
    }

    /**
     * 
     */
    private void initView() {
        RelativeLayout.LayoutParams layoutParams = new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.MATCH_PARENT, RelativeLayout.LayoutParams.WRAP_CONTENT);

        this.setLayoutParams(layoutParams);

        RelativeLayout.LayoutParams layoutParamsCenter = new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.MATCH_PARENT, Sizes.getOptimalMenuHeight(this.getContext()));
        layoutParamsCenter.addRule(RelativeLayout.CENTER_IN_PARENT);

        this.relativeLayoutCenter = new RelativeLayout(this.getContext());
        this.relativeLayoutCenter.setLayoutParams(layoutParamsCenter);
        this.relativeLayoutCenter.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO);

        RelativeLayout.LayoutParams layoutParamsImageButtonCenter = new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.WRAP_CONTENT, RelativeLayout.LayoutParams.WRAP_CONTENT);
        layoutParamsImageButtonCenter.addRule(RelativeLayout.CENTER_IN_PARENT);

        this.imageButtonCenter = new ImageButton(this.getContext());
        this.imageButtonCenter.setId(CENTER_ID);
        this.imageButtonCenter.setLayoutParams(layoutParamsImageButtonCenter);
        this.imageButtonCenter.setImageDrawable(Images.loadDrawableFromFile(this.getContext(), Paths.IMAGE_POWER));
        this.imageButtonCenter.setBackground(null);
        this.imageButtonCenter.setOnDragListener(new DropTargetOnDragListener());
        this.imageButtonCenter.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO);

        this.relativeLayoutCenter.addView(imageViewCenter);
        this.relativeLayoutCenter.addView(this.imageButtonCenter);
        this.relativeLayoutCenter.setOnDragListener(new MenuButtonOnDragListener());

        for(View view : this.menuButtons) {
            this.relativeLayoutCenter.addView(this.placeView(view));
        }

        this.addView(this.relativeLayoutCenter);
    }

    /**
     * 
     * @param view
     * @return
     */
    private View placeView(View view) {
        view.measure(0, 0);

        this.imageButtonCenter.measure(0, 0);

        int x = (int)((view.getMeasuredWidth() / 2) + this.radius * Math.cos(this.angle));
        int y = (int)((view.getMeasuredHeight() / 2) + this.radius * Math.sin(this.angle));

        this.angle += this.step;

        int deltaX = view.getMeasuredWidth();
        int deltaY = view.getMeasuredHeight();
        int deltaImageX = this.imageButtonCenter.getMeasuredWidth() / 2;
        int deltaImageY = this.imageButtonCenter.getMeasuredHeight() / 2;
        int xToDraw = ((x - deltaX) - deltaImageX);
        int yToDraw = ((y - deltaY) - deltaImageY);

        RelativeLayout.LayoutParams layoutParams = new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.WRAP_CONTENT, RelativeLayout.LayoutParams.WRAP_CONTENT);
        layoutParams.addRule(RelativeLayout.ABOVE, CENTER_ID);
        layoutParams.addRule(RelativeLayout.RIGHT_OF, CENTER_ID);
        layoutParams.setMargins(xToDraw, 0, 0, yToDraw);

        view.setLayoutParams(layoutParams);
        view.setOnTouchListener(this);

        return view;
    }

    /**
     * 
     */
    @Override
    public void removeAllViews() {
        super.removeAllViews();

        this.relativeLayoutCenter.removeAllViews();
    }
}


/**
 * 
 */
public abstract class MenuDragableButtonsLayout extends RelativeLayout implements OnTouchListener {
    /**
     * 
     */
    protected CallbackDrag callbackDrag = null;

    /**
     * 
     * @param context
     */
    public MenuDragableButtonsLayout(Context context) {
        super(context);
    }

    /**
     * 
     */
    @Override
    public boolean onTouch(View view, MotionEvent event) {
        switch (event.getAction()) {
        case MotionEvent.ACTION_DOWN: {
            View.DragShadowBuilder shadowBuilder = new View.DragShadowBuilder(view);

            view.startDrag(null, shadowBuilder, view, 0);
            view.setVisibility(View.INVISIBLE);
        } break;
        case MotionEvent.ACTION_UP: {
            view.performClick();
            view.setVisibility(View.VISIBLE);
        } break;
        }

        return true;
    }

    /**
     * 
     */
    public class MenuButtonOnDragListener implements OnDragListener {
        /**
         * 
         */
        @Override
        public boolean onDrag(View view, DragEvent event) {
            View sourceView = (View) event.getLocalState();

            float sourceX = sourceView.getX();
            float sourceY = sourceView.getY();
            float dropX = event.getX() - (sourceView.getWidth() / 2);
            float dropY = event.getY() - (sourceView.getHeight() / 2);

            switch(event.getAction()) {
                case DragEvent.ACTION_DRAG_EXITED : {
                    TranslateAnimation animation = new TranslateAnimation(dropX - sourceX, 0, dropY - sourceY, 0);
                    animation.setDuration(300);

                    sourceView.startAnimation(animation);
                    sourceView.setX(sourceX);
                    sourceView.setY(sourceY);
                    sourceView.setVisibility(View.VISIBLE);
                } break;
                case DragEvent.ACTION_DROP : {
                    sourceView.setX(dropX);
                    sourceView.setY(dropY);
                    sourceView.setVisibility(View.VISIBLE);

                    TranslateAnimation animation = new TranslateAnimation(dropX - sourceX, 0, dropY - sourceY, 0);
                    animation.setDuration(300);

                    sourceView.startAnimation(animation);
                    sourceView.setX(sourceX);
                    sourceView.setY(sourceY);
                } break;
            }

            return true;
        }
    }

    /**
     * 
     */
    public class DropTargetOnDragListener implements OnDragListener {
        /**
         * 
         */
        @Override
        public boolean onDrag(View view, DragEvent event) {
            switch(event.getAction()) {
            case DragEvent.ACTION_DROP :
                return callbackDrag.onDragCompleted(view, event);
            default :
                return true;
            }
        }
    }
}

/**
 * 
 */
public interface CallbackDrag {
    /**
     * 
     * @param view
     * @param event
     * @return
     */
    boolean onDragCompleted(final View view, final DragEvent event);
}

像这样使用它:

public class AnyClass implements CallbackDrag {

    private MenuCircularLayout circleView = null;

    /**
     * 
     */
    private void initMenu() {
        String email = this.getEmail();
        Map<String, Boolean> config = this.preferencesSettings.getSettings(email);

        List<View> views = new ArrayList<View>();
        views.add(this.buttonProfile);
        views.add(this.buttonMeasure);

        if(this.circleView != null) {
            this.circleView.removeAllViews();
            this.layoutMenu.removeView(this.circleView);

            this.circleView = null;
        }

        this.circleView = new MenuCircularLayout(this.getActivity(), this, views);
        this.circleView.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO);
        this.layoutMenu.addView(this.circleView);
    }

    /**
     * 
     */
    @Override
    public boolean onDragCompleted(View view, DragEvent event) {
        View sourceView = (View) event.getLocalState();

        sourceView.setVisibility(View.VISIBLE);

        if(sourceView.equals(this.buttonMeasure)) {
            this.performNFCResult();
        } else if(sourceView.equals(this.buttonProfile)) {
            this.performProfile();
        }

        return true;
    }
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

自定义圆形视图。放置视图 的相关文章

  • 通过代码在创建时突出显示 ListView 项目

    我想在创建 listView 时突出显示 ListView 的第一行 0 我尝试了不同的方法 就像您在注释代码中看到的那样 但没有任何效果 这很奇怪 因为 OnItemClickListener 中的突出显示工作正常 它通过 xml 选择器
  • Android:如何暂停和恢复可运行线程?

    我正在使用 postDelayed 可运行线程 当我按下按钮时 我需要暂停并恢复该线程 请任何人帮助我 这是我的主题 protected void animation music6 music4 postDelayed new Runnab
  • 使用同一个侦听器的多个活动

    我有 4 个活动 它们都包含一个 xml 页脚 其中包含 4 个按钮 每个活动一个 我现在想为这些按钮设置 onclicklistener 它是页脚中的自制菜单 问题是 如何使用侦听器以便重用代码 我有两个想法 创建一个实现 onclick
  • 虚拟回调接口

    在 Eclipse 为您创建的来自 Google 的示例主从流代码中 片段中包含以下内容 private Callbacks mCallbacks sDummyCallbacks public interface Callbacks pub
  • 如何在 Android 应用程序中隐藏 Flutterwave API 密钥

    我正在构建一个 Android 应用程序 目前正在将 Flutterwave 集成到我的应用程序中以进行支付 建议我永远不要将 Flutterwave API 密钥放在我的应用程序上 那么我该如何隐藏这些键呢 我正在使用 Retrofit
  • 如何在不使用 Firebase 控制台的情况下发送 Firebase 云消息通知?

    我从新的 Google 通知服务开始 Firebase Cloud Messaging 感谢这段代码https github com firebase quickstart android tree master messaging htt
  • 蓝牙 BLE Android 以最大吞吐量写入外设

    我们公司开发了一个具有蓝牙 BLE 的硬件单元 并且我们在 Nexus 7 2013 中有一个服务应用程序 我们希望使用它向该单元发送固件文件 文件最大可达 500kb BT芯片是德州仪器CC2540 我浏览了大量的页面 并扫描了 Stac
  • Android 和 Google 地图内部片段以及其他控件和 viewpager

    我是android编程新手 我有一个带有 3 个页面 片段 的小应用程序 使用 pageradapter 和 viewpager 在它们之间滑动 其中一个页面包含复选框 和其他控件 和地图 我的问题是程序在启动时崩溃 Fragment co
  • 在光标所在行强制关闭!

    嘿 我正在尝试创建一个应用程序来查找存储在 SQlite 数据库中的 GPS 数据 但我面临一个问题 我构建了一个 DbAdapter 类来创建数据库 现在我尝试使用以下函数从另一个类获取所有数据上的光标 public Cursor fet
  • 在 Android 中加密/解密字符串的简单方法

    我的问题是如何加密String String AndroidId Override public void onCreate Bundle savedInstanceState super onCreate savedInstanceSta
  • 如何在android中的谷歌地图上聚焦标记

    我只是想知道我们是否可以关注 Android 应用程序中添加的标记 如果是 怎么办 或者有没有其他方法可以完成这项任务 可以说我使用下面的代码添加了一个标记 map addMarker new MarkerOptions title tit
  • 我在 android studio 中使用 kotlin 时出现错误

    为什么会出现这个错误 09 12 16 36 31 502 1886 1886 com getloction nourmedhat smartgate getlocation E AndroidRuntime 致命异常 main 进程 co
  • Android - 存储对ApplicationContext的引用

    我有一个静态 Preferences 类 其中包含一些应用程序首选项和类似的内容 可以在那里存储对 ApplicationContext 的引用吗 我需要该引用 以便我可以在不继承 Activity 的类中获取缓存文件夹和类似内容 你使用的
  • 从三点求圆心的算法是什么?

    我在圆的圆周上有三个点 pt A A x A y pt B B x B y pt C C x C y 如何计算圆心 在Processing Java 中实现它 我找到了答案并实施了一个可行的解决方案 pt circleCenter pt A
  • 如何将 JSON 数据从 Android 发送到 php url?

    我想将登录信息从我的应用程序发送到 php url 因为这我的应用程序将崩溃 任何人都可以帮助我解决这个问题 这是我的服务器登录方法 我想将数据发送到此登录方法 Method public method login Parameters 3
  • Android 的 Intent 和 Parcelable 对象

    为什么我需要打包我的对象 即使我只需将其发送到同一任务的另一个线程 实际上 我需要打开一个甚至可以在同一线程 主线程 上运行的活动 换句话说 为什么 Google 不提供一个 startActivity 版本 它采用通用对象广告参数而不是捆
  • XML 配置中的 screenName 不起作用

    我刚刚在我的应用程序中添加了对 Google Analytics 分析 的支持 但我无法
  • onPrepareOptionsMenu 复制 ActionBar 中的项目

    当我使用 onPrepareOptionsMenu 添加菜单项时 该菜单项会在操作栏中复制其自身 我正在使用片段并在主要活动的 ActionBar 中创建初始菜单 如下所示 Override public boolean onCreateO
  • Android 从通知刷新 Activity

    我有一个程序 我在其中调用通知 如果您将其下拉 该通知将启动一个新活动 mNotificationManager NotificationManager getSystemService ns int icon R drawable sta
  • Android 的代码覆盖率[重复]

    这个问题在这里已经有答案了 可能的重复 Android测试代码覆盖率 Eclipse https stackoverflow com questions 3282702 android test code coverage eclipse

随机推荐

  • Java ConcurrentHashMap 集合的模式

    我在多线程应用程序中常用的数据结构是 ConcurrentHashMap 我想在其中保存一组共享相同键的项目 安装特定键值的第一个项目时会出现此问题 我一直使用的模式是 final ConcurrentMap
  • Flowtype - 字符串与字符串枚举不兼容

    我有一个来自选择输入且类型为字符串的值 但是我想将其传递到函数中 更新语言 接收带有类型别名 Language 我面临的问题是 Flow 只允许我打电话更新语言如果我明确地将我的字符串值与枚举字符串进行比较 并且我想使用像 array in
  • C#:使用指针类型作为字段?

    在 C 中 可以声明具有指针类型成员的结构 或类 如下所示 unsafe struct Node public Node NextNode 它是否安全 呃 暂时忽略那个具有讽刺意味的小unsafe标志 使用这个结构 我的意思是在堆上长期存储
  • 使用 VSCode 在 Python 中调试期间读取输入

    这是我在 vs code 中使用的 python 扩展 python 扩展 https marketplace visualstudio com items itemName donjayamanne python 当我使用扩展提供的调试功
  • python 中数据库连接池的最佳解决方案是什么?

    我开发了一些类似于 DAO 的自定义类来满足我的项目的一些非常特殊的要求 该项目是一个不在任何类型的框架内运行的服务器端进程 该解决方案效果很好 只是每次发出新请求时 我都会通过 MySQLdb connect 打开一个新连接 将其切换为使
  • 当我提出自己的异常作为响应时,如何更轻松地抑制以前的异常?

    Consider try import someProprietaryModule except ImportError raise ImportError It appears that
  • 西格网络速度

    这个库对于获取 cpu ram 和其他人员来说非常棒 但是 我不知道如何获得网络速度 这意味着 在一段时间内发送和接收了多少数据 有办法给我这些数据吗 这可能是我正在寻找的东西吗 Tcp stat sigar getTcp stat get
  • Windows 和 OS X 之间 build.gradle 中的相对路径不同

    我的项目有一个包含密钥库文件 file keystore 的文件夹 这是结构 gradle 2 2 taskArtifacts idea copyright libraries app build generated libs src an
  • 如何使视频资源兼容视网膜显示?

    我有一个应用程序 可以在播放 2 秒的电影时加载 目前 该应用程序已在商店上架 并且除此视频外 所有静态内容均符合视网膜显示标准 我有一个用于视网膜显示屏的 960x640 mp4 h 264 编码视频 它在 iPhone 是的 高分辨率
  • 深层链接不起作用 - 离子

    我正在使用 Deeplinks 和 ionic 3 但我创建的 url 不起作用 这是网址 https lucky com prd rm74fEgBB2frzhagYcov https lucky com prd rm74fEgBB2frz
  • 为什么函数(Python)的 __code__ 是可变的

    在昨天的上一个问题中 在评论中 我开始知道在 python 中 code 函数的属性是可变的 因此我可以编写如下代码 def foo print Hello def foo2 print Hello 2 foo foo code foo2
  • JSF“错误 Mac 未验证!” [复制]

    这个问题在这里已经有答案了 我一直在尝试使用 jsf 中的 primefaces 实现一些基本的推送功能 我用过那里的反例http www primefaces org showcase labs push counter jsf http
  • Unity HTML5 错误:找不到编码 1252 数据

    我使用的是 Mac 在 Google Chrome 中执行我的小应用程序时 出现 不支持代码页 1252 的 pb 我正在使用Unity 2017 年 6 月测试版为了整合几何健身房IFClib 的编码为 NET 4 6 现在在最后一个 U
  • 基本里德-所罗门纠错问题

    在存在丢失字节 或多个丢失字节 的情况下 里德 所罗门纠错是否有效 例如 假设它是 12 8 Reed Solomon 码 那么理论上它应该能够纠正 2 个错误 如果位置已知 则可以纠正 4 个擦除 但是 如果仅接收到 11 或 10 个字
  • JavaFX:无法设置绑定值异常

    在我的 javafx 应用程序中 我创建了一个表视图 其中有一些项目 如果项目存在于表视图中 则应启用删除按钮 否则应禁用删除按钮 现在的问题是 当我选择项目并单击删除按钮时 它会引发以下异常 我该如何解决这个问题 这是我的表格视图代码 t
  • 错误代码:1290。MySQL 服务器正在使用 --secure-file-priv 选项运行,因此无法执行此语句

    我遇到了这样的问题 错误代码 1290 MySQL 服务器正在使用 secure file priv 选项运行 因此无法执行此语句当我尝试执行 MySQL 语句 Windows 时 SELECT FROM xxxx WHERE XXX IN
  • 锁问题 - “U”锁与“X”锁

    我有几个关于更新 U 锁和独占 X 锁的问题 1 当资源即将更新时 对资源施加 X 锁 我是否正确 2 我对U锁有点模糊 当读取资源并且 SQL Server 认为稍后可能需要更新资源时应用 U 锁 我是否正确 如果这是正确的 那么只有在事
  • 在单元测试 CI 期间模拟单击 PyQt5 QMessageBox 小部件中的按钮

    如果我们运行下面的最小示例 而不是长篇大论 python3 Python 3 7 6 default Jan 30 2020 09 44 41 GCC 9 2 1 20190827 Red Hat 9 2 1 1 on linux Type
  • 使用 Crypto++ 库以 CBC 模式实现 AES128

    在输入文件中我有 第一行是一个以十六进制编码的密钥 长度为 16 个字节 在第二行加密消息 CBC 模式下的 AES128 在加密消息前面添加随机 iv 这就是我尝试解密的方法 include
  • 自定义圆形视图。放置视图

    昨天我尝试创建一个自定义视图 其中所有元素 像按钮 图像按钮 被放置在一个圆圈周围 间距相等 我的目标是得到这个 我尝试了这段代码 public class CircleView extends RelativeLayout private