创建可下载的自定义主题并在运行时应用它

2023-11-23

我正在制作一个 Android 应用程序,需要允许客户端维护来自服务器的资源,其中包括字符串、可绘制对象等。

我已经创建了一种机制来下载包含所有这些文件的 zip 文件,并且它们能够非常轻松地更改字符串,我还创建了一种允许客户端更改 UI 控件的背景颜色、更改宽度的机制、身高等,但我有一种感觉,一定有更好的方法来创造这一切。

所以我相信真正的问题是:

创建自定义主题、将其部署在服务器上、让应用程序下载它并随后将其应用到应用程序的最佳实践是什么?

我知道如何创建自定义主题以及如何使用应用程序部署它,以及如何在运行时应用它,但这里的问题是资源是预编译的,一旦创建 APK,开发人员就无法更改它们,这将导致需要添加新的主题/绘图/样式/字符串。

我是否需要为所有这些创建自定义机制(从文件系统加载图像、样式、字符串等)并在运行时通过创建自己的控件(例如在构造函数中执行此操作)来应用它们,或者有什么方法可以做到这正确吗:)? (Swiftkey 如何对所有键盘主题执行此操作,以及类似的应用程序如何允许用户下载主题并在之后应用它)?

如果我没有看到类似的问题,我很抱歉,在过去的两天里我真的试图自己找到答案,但我没有找到任何有用的东西,所以这是我获得建设性答案的最后机会:)。

最接近我需要的解决方案是这个答案:使用外部主题文件在运行时更改应用程序主题但我已经实现了该功能,并且我知道我可以像这样更改颜色,但问题是我希望能够更改边框、按钮按下状态等需要除简单颜色值之外的资源的内容: (.

多谢多谢!

附:我还阅读了有关扩展文件的信息,所以这是我在考虑这个问题时需要考虑的事情,还是我需要寻找其他地方? obb 文件的问题是它们必须通过 PlayStore 部署,这对客户端来说并不“完美”,因为他们需要使用 jobb 打包它,并将其部署到 PlayStore,这对他们来说技术性太强,所以他们更喜欢创建一个 zip 文件,将其放在服务器上,应用程序应该完成其余的工作:)。


我最终决定通过创建一个自定义系统来处理可绘制对象、字符串等来解决这个问题,所以现在我有一个名为“ResourceManager”的自定义类,它处理需要加载的内容以及如何加载,主题作为 zip 文件分发,该应用程序下载、提取和以后使用。

在将它们放入 zip 文件之前,我必须自己编译九个补丁图像,我使用此处的“abrc”来完成此操作:http://forum.xda-developers.com/showthread.php?t=785012

我还创建了一个简单的 bash 脚本,该脚本递归地遍历自定义文件夹并使用 abrc 编译所有九个补丁映像。

我还在 ResourceManager 中创建了一个简单的助手,它检查并告诉我屏幕密度,这样我通常可以支持 hdpi、xhdpi 等密度的图像,最后我不会在每次需要时重新创建图像,我将它们保存在 HashMap 的静态列表中,这样我就可以重用我已经创建的那些,这样我希望防止浪费太多手机内存:)。

好了,以上就是简短的内容,如果有人有任何问题请告诉我,我很乐意与任何人分享这段经历。

Cheers!

============ 编辑 ============

这是我最终为此目的编写的类(它下载文件,检查其版本,从 JSON 文件而不是 strings.xml 加载字符串等)

NOTE:这不是一个完整的课程,因此缺少一些部分,但我认为这足以了解我如何解决所有这些问题:)

/**
 * Created by bojank on 7/28/2014.
 * Class that handles custom resources downloaded from server
 */
public class ResourceManager {

    // List of ninePatchImages in the application
    private static ArrayList<HashMap<String, NinePatchDrawable>> ninePatchHashMaps;
    private static ArrayList<HashMap<String, Drawable>> imagesHashMaps;

    private static ImageLoader imageLoader;

    // Context for methods
    public static Context ctx;

    // JSONObject with all strings
    private static JSONObject joString;

    // JSONObject with all styles
    private static JSONObject joStyles;

    // String with current active lang code
    private static String currentLanguage;

    private static String sdcardPath;

    // Private consturctor to prevent creating a class instance
    private ResourceManager() {
    }

    /**
     * Method that returns a translated string for given key
     *
     * @param key String
     * @return String
     */
    public static String getString(String module, String key) {
        String output = ""; //String.format("[%s - %s]", module, key);

        try {
            if (getStringsFile() != null && getStringsFile().getJSONObject(module).has(key))
                output = getStringsFile().getJSONObject(module).getString(key);
        } catch (Exception e) {

            // Force some default language if proper json file is missing for newly added language
            currentLanguage = "en-US";
            Helper.saveLocale(currentLanguage, ctx);
            Helper.logError("ErrorFetchingString", e);
        }

        return output;
    }

    /**
     * Method that returns JSONObject with string resources
     * @return JSONObject
     * @throws JSONException
     */
    public static JSONObject getStringsFile() throws JSONException {

        if (joString == null) {
            String stringFileName = getResourcesPath() + "languages/" + getCurrentLanguage() + "/values.json";
            String languageFile = Helper.readJsonFile(stringFileName);
            if (languageFile != null) {
                joString = new JSONObject(Helper.readJsonFile(stringFileName));
            } else {
                return null;
            }
        }

        return joString.getJSONObject("strings");
    }

    /**
     * Method that returns current language ("sr", "en"...)
     * @return String
     */
    public static String getCurrentLanguage() {

        if (currentLanguage == null)
            currentLanguage = Helper.getCurrentLanguage(ctx);

        return currentLanguage;
    }

    /**
     * Method that resets joString object and currentLanguage on language change
     */
    public static void resetLanguage() {
        joString = null;
        currentLanguage = null;
    }

    /**
     * Method that resets joStyles object on theme change
     */
    public static void resetStyle() {
        joStyles = null;
    }

    /**
     * Method that deletes a directory from filesystem
     * @param path File
     * @return boolean
     */
    public static boolean deleteDirectory(File path) {
        if( path.exists() ) {
            File[] files = path.listFiles();
            for(int i=0; i<files.length; i++) {
                if(files[i].isDirectory()) {
                    deleteDirectory(files[i]);
                }
                else {
                    files[i].delete();
                }
            }
        }
        return(path.delete());
    }

    /**
    * Method that get's the version of assets file
    * @param url String
    */
    public static String getAssetsVersion(String url) throws IOException {
        Helper.logInfo("REQUEST URL:", url);
        OkHttpClient client = new OkHttpClient();

        // set connection timeut to 5min
        client.setConnectTimeout(1, TimeUnit.MINUTES);

        Request request = new Request.Builder()
                .url(url)
                .build();

        Response response = client.newCall(request).execute();
        return response.body().string();
    }

    /**
     * Method that downloads assets file from server
     * @param url String
     * @return String
     * @throws IOException
     */
    public static String getAssetsFile(String url) throws IOException {

        Helper.logInfo("REQUEST URL:", url);
        OkHttpClient client = new OkHttpClient();

        // set connection timeut to 5min
        client.setConnectTimeout(1, TimeUnit.MINUTES);

        Request request = new Request.Builder()
                .url(url)
                .header("User-Agent", MyApplication.USER_AGENT)
                .build();

        Response response = client.newCall(request).execute();
        InputStream inputStreamFile = response.body().byteStream();

        try {

            // Output stream
            String outputFileName = Environment.getExternalStorageDirectory().toString() + "/assets.zip";

            File deleteFile = new File(outputFileName);
            deleteFile.delete();

            OutputStream output = new FileOutputStream(outputFileName);

            byte data[] = new byte[1024];

            int count;

            // writing data to file
            while ((count = inputStreamFile.read(data)) != -1)
                output.write(data, 0, count);

            // flushing output
            output.flush();

            // closing streams
            output.close();
            inputStreamFile.close();

            return outputFileName;

        } catch (Exception e) {
            Helper.logError("Download Resursa", e);
            return "ERROR";
        }
    }

    public static void setStyle(View v, String styleName) {

        try {
            if (styleName == null || styleName.equals("")) {
                if (v instanceof EditText)
                    processStyle(v, getStylesFile().getJSONObject("EditText"));
            } else
                processStyle(v, getStylesFile().getJSONObject(styleName));
        } catch (Exception e) {
            Helper.logError("Setting Styles", e);
        }

    }

    private static void setBackground(View v, Drawable d) {
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) {
            v.setBackgroundDrawable(d);
        } else {
            v.setBackground(d);
        }
    }

    public static JSONObject getStylesFile() throws JSONException {

        if (joStyles == null) {
            String stylesFileName = getResourcesPath() + "styles/properties.json";
            joStyles = new JSONObject(Helper.readJsonFile(stylesFileName));
        }

        return joStyles;

    }

    public static void processStyle(View v, JSONObject joStyle) {

        if(joStyle != null) {
            try {

                // used for layout margins
                LinearLayout.LayoutParams layoutParams = null;

                if (Helper.isValidParameter(joStyle, "backgroundColor"))
                    v.setBackgroundColor(Color.parseColor(joStyle.getString("backgroundColor")));

                if (Helper.isValidParameter(joStyle, "backgroundImage"))
                    setBackground(v, loadNinePatchFromFilesystem(getImagesPath() + joStyle.getString("backgroundImage")));

                if (v instanceof TextView) {

                   applyTextViewParameters(v, joStyle);

                } else if (v instanceof  ListView) {

                    if (Helper.isValidParameter(joStyle, "dividerColor")) {
                        ((ListView) v).setDivider(new ColorDrawable(Color.parseColor(joStyle.getString("dividerColor"))));
                        ((ListView) v).setDividerHeight(Helper.convertDpToPixel(1));
                    }
                    if (Helper.isValidParameter(joStyle, "dividerHeight")) {
                        ((ListView) v).setDividerHeight(Helper.convertDpToPixel(joStyle.getInt("dividerHeight")));
                    }
                } else if (v instanceof UnderlinePageIndicator) {
                    if (Helper.isValidParameter(joStyle, "backgroundColor")) {
                        v.setBackgroundColor(Color.parseColor(joStyle.getString("backgroundColor")));
                    }
                    if (Helper.isValidParameter(joStyle, "selectedColor")) {
                        ((UnderlinePageIndicator) v).setSelectedColor(Color.parseColor(joStyle.getString("selectedColor")));
                    }
                } else if (v instanceof StyleableBackground) {
                    if (Helper.isValidParameter(joStyle, "backgroundColor")) {
                        View background = v.findViewById(R.id.llBackground);
                        if (background != null) {
                            background.setBackgroundColor(Color.parseColor(joStyle.getString("backgroundColor")));
                        }
                    }
                    if (Helper.isValidParameter(joStyle, "borderTopColor")) {
                        View topBorder = v.findViewById(R.id.llTopBorder);
                        if (topBorder != null) {

                            topBorder.setBackgroundColor(Color.parseColor(joStyle.getString("borderTopColor")));

                            if (Helper.isValidParameter(joStyle, "borderTopHeight")) {
                                topBorder.setMinimumHeight(Helper.convertDpToPixel(joStyle.getInt("borderTopHeight")));
                            }
                        }
                    }
                    if (Helper.isValidParameter(joStyle, "borderBottomColor")) {
                        View bottomBorder = v.findViewById(R.id.llBottomBorder);
                        if (bottomBorder != null) {

                            bottomBorder.setBackgroundColor(Color.parseColor(joStyle.getString("borderBottomColor")));

                            if (Helper.isValidParameter(joStyle, "borderBottomHeight")) {
                                bottomBorder.setMinimumHeight(Helper.convertDpToPixel(joStyle.getInt("borderBottomHeight")));
                            }
                        }
                    }
                    if (Helper.isValidParameter(joStyle, "backgroundImage")) {

                        ImageView ivBackgroundImage = (ImageView) v.findViewById(R.id.ivBackgroundImage);
                        if (ivBackgroundImage != null) {
                            BitmapDrawable d = (BitmapDrawable) ResourceManager.loadImageFromFilesystem(ResourceManager.getImagesPath() + joStyle.getString("backgroundImage"));

                            d.setTileModeXY(Shader.TileMode.REPEAT, Shader.TileMode.REPEAT);
                            d.setGravity(Gravity.FILL_HORIZONTAL | Gravity.FILL_VERTICAL);
                            setBackground(ivBackgroundImage, d);
                        }
                    }
                }

                if(Helper.isValidParameter(joStyle, "width"))
                    v.setMinimumWidth(joStyle.getInt("width"));

                if(Helper.isValidParameter(joStyle, "height"))
                    v.setMinimumHeight(joStyle.getInt("height"));

                if(Helper.isValidParameter(joStyle, "padding"))
                    v.setPadding(joStyle.getInt("padding"), joStyle.getInt("padding"), joStyle.getInt("padding"), joStyle.getInt("padding"));

                if(Helper.isValidParameter(joStyle, "paddingLeft"))
                    v.setPadding(joStyle.getInt("paddingLeft"), v.getPaddingTop(), v.getPaddingRight(), v.getPaddingBottom());

                if(Helper.isValidParameter(joStyle, "paddingTop"))
                    v.setPadding(v.getPaddingLeft(), joStyle.getInt("paddingTop"), v.getPaddingRight(), v.getPaddingBottom());

                if(Helper.isValidParameter(joStyle, "paddingRight"))
                    v.setPadding(v.getPaddingLeft(), v.getPaddingTop(), joStyle.getInt("paddingRight"), v.getPaddingBottom());

                if(Helper.isValidParameter(joStyle, "paddingBottom"))
                    v.setPadding(v.getPaddingLeft(), v.getPaddingTop(), v.getPaddingRight(), joStyle.getInt("paddingBottom"));

                if(Helper.isValidParameter(joStyle, "margin")) {
                    layoutParams = new LinearLayout.LayoutParams(v.getLayoutParams());
                    layoutParams.setMargins(joStyle.getInt("margin"), joStyle.getInt("margin"), joStyle.getInt("margin"), joStyle.getInt("margin"));
                }

                if(Helper.isValidParameter(joStyle, "marginLeft")) {
                    layoutParams = new LinearLayout.LayoutParams(v.getLayoutParams());
                    layoutParams.setMargins(joStyle.getInt("marginLeft"), layoutParams.topMargin, layoutParams.rightMargin, layoutParams.bottomMargin);
                }

                if(Helper.isValidParameter(joStyle, "marginTop")) {
                    layoutParams = new LinearLayout.LayoutParams(v.getLayoutParams());
                    layoutParams.setMargins(layoutParams.leftMargin, joStyle.getInt("marginTop"), layoutParams.rightMargin, layoutParams.bottomMargin);
                }

                if(Helper.isValidParameter(joStyle, "marginRight")) {
                    layoutParams = new LinearLayout.LayoutParams(v.getLayoutParams());
                    layoutParams.setMargins(layoutParams.leftMargin, layoutParams.topMargin, joStyle.getInt("marginRight"), layoutParams.bottomMargin);
                }

                if(layoutParams != null)
                    v.setLayoutParams(layoutParams);


                RelativeLayout.LayoutParams relativeLayoutParams = null;

                if (Helper.isValidParameter(joStyle, "alignParentTop") && joStyle.getBoolean("alignParentTop")) {
                    relativeLayoutParams = new RelativeLayout.LayoutParams(v.getLayoutParams());
                    relativeLayoutParams.addRule(RelativeLayout.ALIGN_PARENT_TOP);
                }

                if (Helper.isValidParameter(joStyle, "alignParentLeft") && joStyle.getBoolean("alignParentLeft")) {
                    relativeLayoutParams = new RelativeLayout.LayoutParams(v.getLayoutParams());
                    relativeLayoutParams.addRule(RelativeLayout.ALIGN_PARENT_LEFT);
                }

                if (Helper.isValidParameter(joStyle, "alignParentBottom") && joStyle.getBoolean("alignParentBottom")) {
                    relativeLayoutParams = new RelativeLayout.LayoutParams(v.getLayoutParams());
                    relativeLayoutParams.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM);
                }

                if (Helper.isValidParameter(joStyle, "alignParentRight") && joStyle.getBoolean("alignParentRight")) {
                    relativeLayoutParams = new RelativeLayout.LayoutParams(v.getLayoutParams());
                    relativeLayoutParams.addRule(RelativeLayout.ALIGN_PARENT_RIGHT);
                }

                if(Helper.isValidParameter(joStyle, "marginLeft")) {
                    relativeLayoutParams = new RelativeLayout.LayoutParams(v.getLayoutParams());
                    relativeLayoutParams.setMargins(joStyle.getInt("marginLeft"), relativeLayoutParams.topMargin, relativeLayoutParams.rightMargin, relativeLayoutParams.bottomMargin);
                }

                if(Helper.isValidParameter(joStyle, "marginTop")) {
                    relativeLayoutParams = new RelativeLayout.LayoutParams(v.getLayoutParams());
                    relativeLayoutParams.setMargins(relativeLayoutParams.leftMargin, joStyle.getInt("marginTop"), relativeLayoutParams.rightMargin, relativeLayoutParams.bottomMargin);
                }

                if(Helper.isValidParameter(joStyle, "marginRight")) {
                    relativeLayoutParams = new RelativeLayout.LayoutParams(v.getLayoutParams());
                    relativeLayoutParams.setMargins(relativeLayoutParams.leftMargin, relativeLayoutParams.topMargin, joStyle.getInt("marginRight"), relativeLayoutParams.bottomMargin);
                }

                if (relativeLayoutParams != null) {
                    v.setLayoutParams(relativeLayoutParams);
                }

            } catch (Exception e) {
                Helper.logError("", e);
            }
        }
    }

    public static String getSdcardPath() {

        if(sdcardPath == null)
            sdcardPath = ctx.getApplicationInfo().dataDir;

        return sdcardPath;
    }

    public static String getResourcesPath() {

        return getSdcardPath() + "/resources/";
    }

    public static String getCSSPath() {

        return getResourcesPath() + "default.css";
    }

    public static String getImagesPath() {

        return getResourcesPath() + "images/" + ResourceConstants.getScreenDPI(ctx) + "/";
    }

    public static String getImagesPathNoDpi() {

        return getResourcesPath() + "images/";
    }

    public static NinePatchDrawable loadNinePatchFromFilesystem(String filename) {

        if(ninePatchHashMaps == null)
            ninePatchHashMaps = new ArrayList<HashMap<String, NinePatchDrawable>>();

        // check if we already have this filename so we can reuse it
        for (int i = 0; i < ninePatchHashMaps.size(); i++) {
            HashMap<String, NinePatchDrawable> row = ninePatchHashMaps.get(i);

            if(row.containsKey(filename))
                return row.get(filename);
        }

        NinePatchDrawable patchy = null;
        try {
            BitmapFactory.Options options = new BitmapFactory.Options();
            options.inPreferredConfig = Bitmap.Config.ARGB_8888;
            Bitmap bitmap = BitmapFactory.decodeFile(filename, options);
            byte[] chunk = bitmap.getNinePatchChunk();
            boolean result = NinePatch.isNinePatchChunk(chunk);
            if (result)
                patchy = new NinePatchDrawable(bitmap, chunk, new Rect(), null);
        } catch (Exception e){
            Helper.logError("NinePatchLoading",e);
        }

        if(patchy != null) {

            HashMap<String, NinePatchDrawable> drawableImage = new HashMap<String, NinePatchDrawable>();
            drawableImage.put(filename, patchy);
            ninePatchHashMaps.add(drawableImage);
        }

        return patchy;

    }

    public static Drawable loadImageFromFilesystem(String filename) {

        if(imagesHashMaps == null)
            imagesHashMaps = new ArrayList<HashMap<String, Drawable>>();

        // check if we already have this filename so we can reuse it
        for (int i = 0; i < imagesHashMaps.size(); i++) {
            HashMap<String, Drawable> row = imagesHashMaps.get(i);

            if(row.containsKey(filename))
                return row.get(filename);
        }

        Drawable image = null;
        try {
            BitmapFactory.Options options = new BitmapFactory.Options();
            options.inPreferredConfig = Bitmap.Config.ARGB_8888;
            Bitmap bitmap = BitmapFactory.decodeFile(filename, options);

            if(bitmap == null)
                bitmap = BitmapFactory.decodeFile(filename.replace(ResourceConstants.getScreenDPI(ctx) + "/", ""), options);

            image = new BitmapDrawable(bitmap);
        } catch (Exception e){
            Helper.logError("ImageLoadingError",e);
        }

        if(image != null) {
            HashMap<String, Drawable> drawableImage = new HashMap<String, Drawable>();
            drawableImage.put(filename, image);
            imagesHashMaps.add(drawableImage);
        }

        return image;
    }

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

创建可下载的自定义主题并在运行时应用它 的相关文章

  • Android - 如何一次只允许播放一个 MediaPlayer 实例?

    我正在尝试创建一个简单的 Sound board Android 应用程序 使用 ListView 项目作为按钮 顺便说一句 我是一个新手程序员 我的想法是 我按下一个按钮 就会播放一个特定的声音文件 如果我在播放声音时按下任何按钮 它应该
  • 从 Throwable 获取错误代码 - Android

    我怎样才能从错误代码可投掷 https developer android com reference java lang Throwable html public void onFailure Throwable exception 我
  • Android第一次动画不流畅

    我正在尝试一个动画将 imageView 从屏幕底部滑动到屏幕中心 但是当我第一次执行此动画时 它不平滑 但当第二次执行动画时 它是正常且平滑的 我几乎尝试了所有方法 但无法解决我的问题 这是我的动画文件
  • 如何将安卓手机从睡眠状态唤醒?

    如何以编程方式将 Android 手机从睡眠状态唤醒 挂起至内存 我不想获取任何唤醒锁 这意味着手机在禁用 CPU 的情况下进入 真正的 睡眠状态 我想我可以使用某种RTC 实时时钟 机制 有人有例子吗 Thanks 为了让Activity
  • 如果我们使用后退按钮退出,为什么 Android 应用程序会重新启动?

    按住主页按钮并返回应用程序时 应用程序不会重新启动 为什么使用后退按钮会重新启动 如果我们使用后退按钮退出 有什么方法可以解决在不重新启动的情况下获取应用程序的问题吗 请帮忙 当您按下Home按钮 应用程序将暂停并保存当前状态 最后应用程序
  • 在 Android 中使用 DataOutputStream 在 POST 正文中发送特殊字符 (ë ä ï)

    我目前正在开发一个具有大量服务器端通信的 Android 应用程序 昨天 我收到一份错误报告 称用户无法发送 简单 特殊字符 例如 我搜索过但没有找到任何有用的东西 可能重复 没有答案 https stackoverflow com que
  • 获取 AlarmManager 中活动的 PendingIntents 列表

    我有办法获取活动列表PendingIntent在设备中 我开始工作AlarmManager我想看看我的PendingIntents 已正确创建和删除 也很高兴看到其他什么PendingIntent在那里 只是为了看看某些应用程序是否正在做一
  • 在我的Android中,当其他应用程序想要录制音频时如何停止录音?

    在我的应用程序中 服务通过 AudioRecord 持续录制音频 当我的应用程序运行时 其他与音频记录相关的应用程序 例如 Google 搜索 无法工作 如何知道何时有其他应用想要录制音频 以便我可以停止录制以释放资源 答案是MediaRe
  • Firebase:如何在Android应用程序中设置默认通知渠道?

    如何设置default通知渠道通知消息当应用程序在后台运行时会出现什么情况 默认情况下 这些消息使用 杂项 通道 如你看到的在官方文档中 https firebase google com docs cloud messaging andr
  • Flutter 深度链接

    据Flutter官方介绍深层链接页面 https flutter dev docs development ui navigation deep linking 我们不需要任何插件或本机 Android iOS 代码来处理深层链接 但它并没
  • Android 启动器快捷方式

    我制作了一个简单的打卡 打卡时钟应用程序 我想向用户添加在主屏幕上创建快捷方式的选项 该快捷方式将切换应用程序的状态 超时 超时 但我根本不希望此快捷方式在屏幕上打开应用程序 这是我的 setupShortcut private void
  • 如何在C(Linux)中的while循环中准确地睡眠?

    在 C 代码 Linux 操作系统 中 我需要在 while 循环内准确地休眠 比如说 10000 微秒 1000 次 我尝试过usleep nanosleep select pselect和其他一些方法 但没有成功 一旦大约 50 次 它
  • Android Studio:无法启动守护进程

    当我尝试在 Android Studio 中导入 gradle 项目时 遇到以下错误 Unable to start the daemon process This problem might be caused by incorrect
  • Android构建apk:控制MANIFEST.MF

    Android 构建 APK 假设一个 apk 包含一个库 jar 例如 foo jar 该库具有 META INF MANIFEST MF 这对于它的运行很重要 但在APK中有一个包含签名数据的MANIFEST MF 并且lib jar
  • 如何创建像谷歌位置历史记录一样的Android时间轴视图?

    我想设计像谷歌位置历史这样的用户界面 我必须为我正在使用的应用程序复制此 UIRecyclerView 每行都是水平的LinearLayout其中包含右侧的图标 线条和视图 该线是一个FrameLayout具有圆形背景和半透明圆圈Views
  • Android中webview的截图方法

    我在 webview 中的 html5 canvas 上画了一些线 并尝试使用下面的代码截取 webview 的屏幕截图 WebView webView WebView findViewById R id webview webView s
  • Android:有没有办法以毫安为单位获取设备的电池容量?

    我想获取设备的电池容量来进行一些电池消耗计算 是否可以以某种方式获取它 例如 三星 Galaxy Note 2 的电池容量为 3100mAh 谢谢你的帮助 知道了 在 SDK 中无法直接找到任何内容 但可以使用反射来完成 这是工作代码 pu
  • 使用 Espresso 检查 EditText 的字体大小、高度和宽度

    如何使用 Espresso 检查 EditText 的字体大小 高度和宽度 目前要分割我使用的文本 onView withId R id editText1 perform clearText typeText Amr 并阅读文本 onVi
  • 如何将图像从 Android 应用程序上传到网络服务器的特定文件夹中

    如何将图像从 android 移动到 Web 服务器上的指定文件夹 这是我的安卓代码 package com example bitmaptest import java io ByteArrayOutputStream import ja
  • 找到 Android 浏览器中使用的 webkit 版本?

    有没有办法知道某些特定手机上的 Android 浏览器使用的是哪个版本的 webkit 软件 如果有一个您可以浏览以获取该信息的 URL 那就太好了 但任何其他方式也很好 如果你知道 webkit 版本 你就知道 html5 支持多少 至少

随机推荐

  • 使用 Boost::Regex 获取最后一场比赛

    我在 C 中有一个带有 Boost 的正则表达式 它匹配多行字符串中的行 正则表达式搜索找到第一个匹配项 但是我对匹配的最后一行感兴趣 我现在使用的代码是这样的 matched boost regex search input result
  • 静态锁可以跨不同的子类工作吗?

    如果我有 abstract class Parent static object staticLock new object public void Method1 lock staticLock Method2 protected abs
  • 如何将gradle添加到子项目根项目作为依赖项?

    我有以下项目结构settings gradle include B include C rootProject name A 如何将gradle添加到子项目根项目作为依赖项 就project就方法而言 根项目没有名称 这是项目 B 的 bu
  • 计算总和为零的所有连续子数组

    给定长度为 n 的随机数 正数和负数 数组 我想要数字连续的子数组 其总和等于零 Example 鉴于我有数组a 1 1 2 2 6 6 输出将是6因为子数组如下 1 1 2 2 6 6 1 1 2 2 2 2 6 6 1 1 2 2 6
  • 使用 JPA 注释映射 java.util.Map 时键和值列名称会覆盖

    我正在研究使用 Hibernate 4 1 9 和 JPA 注释来注释地图的不同方法 如果我想存储一个 Map 其中键是实体值的属性 则标记如下所示 OneToMany mappedBy deptById targetEntity com
  • C# 属性集合[重复]

    这个问题在这里已经有答案了 我有一个特定的内置属性集合 例如 System Runtime Serialization SerializedAttribute 我想将其应用于特定的类集合 是否有可能将这些属性合而为一 我不想将它们全部显式应
  • 在一张图上绘制两张图。功能线不起作用

    我想在一张图上绘制两张图 我读这个帖子 但函数lines不起作用 什么也没有发生 我不知道会出现什么问题 有任何想法吗 编辑 可重现的例子 gt tr error 1 0 2314984 0 2314990 0 2314981 0 2314
  • MIME 类型('text/html')不是受支持的样式表

    我正在将 css 文件链接到我的express handlebars 文件 但收到此错误 拒绝应用来自 的样式http localhost 4000 cs366 style draft css 因为它的 MIME 类型 text html
  • 退出当前浏览器(返回一级)

    有时你会抛出多个browser进入一个函数进行调试 我知道你可以退出整个shebangQ但是如果您想退出第二个浏览器 参见下面的代码 并返回到第一级浏览器怎么办 我听说过类型c但这并没有退出第二层browser FUN lt functio
  • 将变量从一个 HTML 页面传递到另一个 HTML 页面的最佳实践是什么?

    我对网络应用程序编程比较陌生 所以我希望这个问题对每个人来说都不是太基础 我创建了一个 HTML 页面 其中包含一个包含 dojox 数据网格 v1 2 的 FORM 其中填充了不同杂货项目的描述行 用户选择他感兴趣的项目后 他将单击 提交
  • UIImagePickerController 的cameraViewTransform 忽略了 iOS 10 beta 上的“缩放”和“平移”

    我一直在使用下面的代码来缩放 UIImagePickerController 的实时预览以填充整个屏幕 到目前为止 这一切都很完美 几天前 我在 iPhone 5 上安装了 iOS 10 beta 7 但它不再扩展了 我可以在 UIImag
  • PCL .NET 4.5 定时器

    我正在使用 Xamarin 和 MvvmCross 构建跨平台应用程序 我需要每分钟调用服务器更新一次 稍后我将转向推送通知 但我无法在我的核心项目中设置计时器 我见过 MvvmCross N 42 但我相信目标项目比较旧 允许计时器 下面
  • Translation.E004) 您为 LANGUAGE_CODE 设置提供了一个不在 LANGUAGES 设置中的值

    我刚刚重新映像了我的计算机 因此为我的 django 项目设置了所有内容 然后在运行服务器时出现此错误 我不知道它指的是什么 因为我的原始网站是开箱即用的 而且我会说英语 并且该网站是一个内部工作网站 因此没有理由做任何基于语言的事情 有谁
  • Angular 2 中的错误处理程序和拦截器有什么区别?

    另外 对于实时 Web 应用程序 ng2 中前端错误处理的最佳解决方案是什么 使用 Http拦截器 进行前端错误处理可以吗 请解释一下这些事情 Angular 2 中的错误处理程序和拦截器有什么区别 根据 Angular 文档 它们是这样定
  • 在 Ubuntu 上更新 TimescaleDB 后 Postgres 出现错误:找不到文件

    如指定官方文档我将 Timescale PPA 存储库添加到apt 然后我通过安装它sudo apt install timescaledb postgresql 9 6 一切都工作正常 随着最近的运行sudo apt upgrade时间刻
  • 更改当前正在运行的进程的所有者

    我有一个当前正在运行的进程pid PID 并由user foo这是not root 我想将此进程的所有权转移给另一个进程user bar这也是not root 是否有一个 shell 命令可以更改进程的所有者 我正在考虑一个chown但对于
  • Stripe:为订阅添加免费天数

    是否可以为 Stripe 上的有效订阅添加免费天数 我想这样做是为了创建一个推荐计划 当用户推荐某人时 他将获得 10 天的免费时间 我认为一个好方法是更新current period endvar 但我在文档中找不到如何执行此操作 例如
  • Array.Sum() 导致溢出

    我有一个像这样的 int 数组 int arr 256741038 623958417 467905213 714532089 938071625 然后我创建了一个 int64 var Int64 sum arr Sum 但这导致了溢出 运
  • Delphi XE - RawByteString 与 AnsiString

    我在这里有一个类似的问题 Delphi XE 我应该使用 String 还是 AnsiString 在决定在我的 大型 库中使用 ANSI 字符串是正确的之后 我意识到我实际上可以使用 RawByteString 而不是 ANSI 因为我将
  • 创建可下载的自定义主题并在运行时应用它

    我正在制作一个 Android 应用程序 需要允许客户端维护来自服务器的资源 其中包括字符串 可绘制对象等 我已经创建了一种机制来下载包含所有这些文件的 zip 文件 并且它们能够非常轻松地更改字符串 我还创建了一种允许客户端更改 UI 控