美文网首页菜菜Android
Java 纯代码创建安卓页面布局(含异步加载图片)

Java 纯代码创建安卓页面布局(含异步加载图片)

作者: 缺水的海豚 | 来源:发表于2016-10-04 18:10 被阅读245次

    开发环境

    • macOS Sierra (10.12)
    • Android Studio 2.2 (minSDK 5.1)

    总体效果

    总体效果图

    说明:
    本示例采用的是Fragment实现的
    列表中的分割线,是采用的 LinearLayout 自带的 divider 实现的

    界面结构(Layout文件)

    在 Fragment 中,采用 ScrollView + LinearLayout 实现,代码如下所示:

    <ScrollView
        xmlns:android="http://schemas.android.com/apk/res/android"    
        xmlns:tools="http://schemas.android.com/tools"    
        android:layout_width="match_parent"    
        android:layout_height="match_parent"    
        android:scrollbars="vertical"    
        tools:context=".Fragment.HomeFrg">    
        <LinearLayout        
            android:id="@+id/frg_home"        
            android:layout_width="match_parent"        
            android:layout_height="match_parent"        
            android:orientation="vertical"        
            android:divider="@drawable/sep_home"        
            android:showDividers="middle" />
    </ScrollView>
    

    divider 的实现详见上述代码中,LinearLayout 的最后两行的属性声明。
    文件目录如下:


    divider 文件目录

    sep_home.xml 文档结构如下:

    <?xml version="1.0" encoding="utf-8"?>
    <shape xmlns:android="http://schemas.android.com/apk/res/android">
        <solid android:color="@color/colorBG" />    
        <size android:height="10dp" />
    </shape>
    

    背景颜色定义在 values/colors 中,如下所示:

    <color name="colorBG">#EEEEEE</color>
    

    说明:divider 采用 Drawable 的 shape 来实现,在 shape 中,一定要添加 solid 和 size 两个元素,即使是透明背景色,也要添加这两个元素。并且,颜色的设置也要明文声明。

    至此,界面布局的 xml 文件就准备完毕了,接下来开始些功能实现。

    代码结构(Java 文件)

    关于加载图片,在现在(新版本)的安卓开发过程中,如果在主线程直接加载网络图片,会报 NetworkOnMainThreadException 异常。
    所以,这里采用了异步(结合线程池)加载的方式来进行。
    当然,也可以采用成熟的第三方组件,如:Picasso(主页Github) 等。
    本文自行实现,先创建一个自定义的图像类,如下所示:

    import android.graphics.drawable.Drawable;
    import android.os.Handler;
    
    import java.lang.ref.SoftReference;
    import java.net.URL;
    import java.util.HashMap;
    import java.util.Map;
    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    
    public class TestImage {
        // 为了加快速度,在内存中开启缓存
        // 主要应用于重复图片较多时,或者同一个图片要多次被访问,比如在ListView时来回滚动
        public Map<String, SoftReference<Drawable>> imageCache = new HashMap<String, SoftReference<Drawable>>();
    
        // 固定 10 个线程来执行任务
        private ExecutorService _exeService = Executors.newFixedThreadPool(10);
    
        private final Handler _handler = new Handler();
    
        public Drawable getImage(final String url, final Callback callback) {
    
            // 缓存中存在就用缓存中的图片
            if (imageCache.containsKey(url)) {
                SoftReference<Drawable> softReference = imageCache.get(url);
    
                if (softReference.get() != null) {
                    return softReference.get();
                }
            }
    
            // 缓存中没有图片,就从网络中获取图片,同时,存入缓存
            _exeService.submit(new Runnable() {
                @Override
                public void run() {
                    final Drawable drawable = getImage(url);
                    imageCache.put(url, new SoftReference<Drawable>(drawable));
    
                    _handler.post(new Runnable() {
                        @Override
                        public void run() {
                            callback.imageLoaded(drawable);
                        }
                    });
                }
            });
    
            return null;
        }
    
        // 从网络中获取图片
        protected Drawable getImage(String url) {
            Drawable drawable = null;
    
            try {
                drawable = Drawable.createFromStream(new URL(url).openStream(), "img.png");
            } catch (Exception e) {
                e.printStackTrace();
            }
    
            return drawable;
        }
    
        // 回调接口
        public interface Callback {
            void imageLoaded(Drawable drawable);
        }
    }
    

    特别说明:用 final 参数的原因是:防止方法参数在调用时被篡改

    图像类建立好了之后,就可以在主类中调用了。
    接下来,就是在主程序中调用了。
    同时,在主程序(Fragment)中,采用动态创建各元素的方式来进行布局,全部代码如下:

    public class HomeFrg extends Fragment {
    
        private LinearLayout _layout;
        //private TestImage _testImage = new TestImage();
    
        public HomeFrg() {
            // Required empty public constructor
        }
    
        @Override
        public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
            View view = inflater.inflate(R.layout.frg_home, container, false);
            initView(view);
    
            // Inflate the layout for this fragment
            return view;
        }
    
        public void setMenuVisibility(boolean menuVisible) {
            super.setMenuVisibility(menuVisible);
    
            if (getView() != null) {
                getView().setVisibility(menuVisible ? View.VISIBLE : View.GONE);
            }
        }
    
        private void initView(View view) {
            _layout = (LinearLayout) view.findViewById(R.id.frg_home);
    
            // TODO:从数据库获取数据
    
            // 这里直接循环 3 次来进行界面效果的展示
            for (int i = 0; i < 3; i++) {
                initCell(view);
            }
        }
    
        private void initCell(View view) {
            Context self = this.getContext();
    
            // 创建单元格(RelativeLayout)
            RelativeLayout.LayoutParams layoutWrapper = new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
            RelativeLayout wrapper = new RelativeLayout(self);
            wrapper.setBackgroundColor(Helper.getColor(self, R.color.colorWhite));
            wrapper.setPadding(0, 30, 0, 30);
            _layout.addView(wrapper, layoutWrapper);
    
            // 创建封面图片(ImageView)
            RelativeLayout.LayoutParams layoutCover = new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 600);
            ImageView imgCover = new ImageView(self);
            int idCover = view.generateViewId();   // 为了依次分开排列各控件,需设定控件的 Id,这里用 view.generateViewId() 自动产生
            imgCover.setId(idCover);
            loadImage("http://pic9.nipic.com/20100904/4845745_195609329636_2.jpg", imgCover);   // 异步加载任意网络图片(用于测试)
            imgCover.setScaleType(ImageView.ScaleType.CENTER_CROP);
            imgCover.setPadding(20, 0, 20, 0);
            wrapper.addView(imgCover, layoutCover);
    
            // 创建标题(TextView)
            RelativeLayout.LayoutParams layoutTitle = new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
            layoutTitle.setMargins(20, 0, 20, 0);
            layoutTitle.addRule(RelativeLayout.BELOW, idCover);   // 在上一个元素的下方进行呈现,否则,在界面中,两个元素会重叠在一起
            TextView txtTitle = new TextView(self);
            int idTitle = view.generateViewId();
            txtTitle.setId(idTitle);
            txtTitle.setText("标题内容标题内容标题内容标题内容标题内容标题内容");
            txtTitle.setTextSize(20);
            txtTitle.setEllipsize(TextUtils.TruncateAt.END);   // 末尾多余字符用省略号代替
            txtTitle.setSingleLine();   // 设置单行显示
            txtTitle.setTextColor(Helper.getColor(self, R.color.colorBlack));
            wrapper.addView(txtTitle, layoutTitle);
    
            // 创建作者(TextView)
            RelativeLayout.LayoutParams layoutAuthor = new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
            layoutAuthor.setMargins(20, 0, 20, 0);
            layoutAuthor.addRule(RelativeLayout.BELOW, idTitle);
            TextView txtAuthor = new TextView(self);
            int idAuthor = view.generateViewId();
            txtAuthor.setId(idAuthor);
            txtAuthor.setText("作者名称");
            txtAuthor.setTextColor(Helper.getColor(self, R.color.colorBlack));
            wrapper.addView(txtAuthor, layoutAuthor);
    
            // 创建日期(TextView)
            RelativeLayout.LayoutParams layoutTime = new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
            layoutTime.setMargins(20, 0, 20, 0);
            layoutTime.addRule(RelativeLayout.BELOW, idAuthor);
            TextView txtTime = new TextView(self);
            txtTime.setText("2016年9月22日 16:33");
            wrapper.addView(txtTime, layoutTime);
        }
    
        // 再次封装 TestImage 类,方便本页面进行调用
        private void loadImage(String url, final ImageView imageView) {
            Drawable imgCache = new TestImage().getImage(url, new TestImage.Callback() {
                @Override
                public void imageLoaded(Drawable drawable) {
                    imageView.setImageDrawable(drawable);
                }
            });
    
            if (imgCache != null) {
                imageView.setImageDrawable(imgCache);
            }
        }
    }
    

    至此,所有 Java 代码实现完毕。

    写在最后
    在 RelativeLayout 的代码布局中,可通过 LayoutParams 的 setMargins 和控件自身的 setPadding 来进行各处留白距离的微调。

    相关文章

      网友评论

        本文标题:Java 纯代码创建安卓页面布局(含异步加载图片)

        本文链接:https://www.haomeiwen.com/subject/hpxsyttx.html