基于Android的本地电子书阅读器的设计与实现Ebook(2)

基于Android的本地电子书阅读器的设计与实现Ebook(2)

接着上回,我们继续分说下面步骤。
用户登录后,映入眼帘的就是我们实现阅读器的主要三个界面,分别是书架、感悟、分类。

首先这三个界面的切换是通过Fragment实现的底部导航:
请添加图片描述
activity_main_login.xml:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".MainLogin">

    <androidx.viewpager.widget.ViewPager
        android:id="@+id/viewPager"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1">
    </androidx.viewpager.widget.ViewPager>

    <com.google.android.material.bottomnavigation.BottomNavigationView
        android:id="@+id/bottomNavigationView"
        app:menu="@menu/menu_main"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="@drawable/bookbg"
        app:itemIconTint="@drawable/fgbtn"
        app:labelVisibilityMode="auto"/>
</LinearLayout>

这里的menu是在res文件夹下创建的menu文件夹,具体存放图标和文字
menu_main.xml:

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
    <item
        android:id="@+id/item1"
        android:title="书架"
        android:icon="@drawable/book"/>
    <item
        android:id="@+id/item2"
        android:title="感悟"
        android:icon="@drawable/note"/>
    <item
        android:id="@+id/item3"
        android:title="分类"
        android:icon="@drawable/sort"/>
</menu>

接下来是java文件MainLogin:

 protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main_login);

        viewPager=findViewById(R.id.viewPager);
        bottomNavigationView=findViewById(R.id.bottomNavigationView);

        //初始化fragments数组
        list=new ArrayList<Fragment>();
        list.add(new LoginFragment1());
        list.add(new LoginFragment2());
        list.add(new LoginFragment3());

        //设置底部导航点击和不点击时颜色
        Resources resource = getResources();
        @SuppressLint("ResourceType")
        ColorStateList csl = resource.getColorStateList(R.drawable.fgbtn);
        bottomNavigationView.setItemTextColor(csl);

        viewPager.setAdapter(new MyAdapter(getSupportFragmentManager(),list));

        //点击底部导航项,显示对应的页面
        bottomNavigationView.setOnNavigationItemSelectedListener(
                new BottomNavigationView.OnNavigationItemSelectedListener() {
                    @Override
                    public boolean onNavigationItemSelected(@NonNull MenuItem item) {
                        switch (item.getItemId()){
                            case R.id.item1:
                                viewPager.setCurrentItem(0);
                                break;
                            case R.id.item2:
                                viewPager.setCurrentItem(1);
                                break;
                            case R.id.item3:
                                viewPager.setCurrentItem(2);
                                break;
                        }
                        return true;

                    }
                });

        //页面左右滑动时,让底部的导航项和显示的页面保持一致
        viewPager.setOnPageChangeListener(new ViewPager.OnPageChangeListener() {
            @Override
            public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {

            }

            @Override
            public void onPageSelected(int position) {
                if(menuItem==null){
                    menuItem=bottomNavigationView.getMenu().getItem(0);
                }
                //将上次的选择设置为false,等待下次的选择
                menuItem.setChecked(false);
                menuItem=bottomNavigationView.getMenu().getItem(position);
                menuItem.setChecked(true);
            }

            @Override
            public void onPageScrollStateChanged(int state) {

            }
        });
    }

    //作为fragment的适配器
    private class MyAdapter extends FragmentPagerAdapter
    {
        List list;
        public MyAdapter(@NonNull FragmentManager fm, List list) {
            super(fm);
            this.list=list;
        }

        @NonNull
        @Override
        public Fragment getItem(int position)

        {
            return (Fragment) list.get(position);
        }

        @Override
        public int getCount()
        {
            return list.size();
        }
    }
}

现在我们的底部导航已经建好了,接下来需要我们新建三个fragment页面,分别实现书架、感悟、分类三个部分。

首先是书架部分,先上图看看效果:
请添加图片描述
fragment_login1.xml:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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:orientation="horizontal"
    tools:context=".LoginFragment1">

    <LinearLayout
        android:layout_weight="1"
        android:layout_width="0dp"
        android:layout_height="match_parent"
        android:orientation="vertical">

        <com.example.ebook.MarqueTextView
            android:layout_width="wrap_content"
            android:layout_height="50dp"
            android:id="@+id/mtv"
            android:ellipsize="marquee"
            android:textSize="25dp"
            android:singleLine="true"
            android:marqueeRepeatLimit="marquee_forever"
            android:background="@drawable/bookbg"/>

        <ListView
            android:id="@+id/lg3_listView"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:background="@drawable/bookbg2"/>
    </LinearLayout>


</LinearLayout>

在这里说明一下,页面的顶部本质是一个TextView控件,但文字是可以横向滚动的,所以得自己写一个基于TextView的子类。
java文件MarqueTextView:

public class MarqueTextView extends androidx.appcompat.widget.AppCompatTextView {

    public MarqueTextView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }

    public MarqueTextView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public MarqueTextView(Context context) {
        super(context);
    }

    @Override

    public boolean isFocused() {
        //就是把这里返回true即可
        return true;
    }
}

然后是实现书架这一部分的java文件LoginFragment1:

public class LoginFragment1 extends Fragment implements AdapterView.OnItemClickListener{
    //变量名注册
    ListView listView;
    SimpleAdapter simpleAdapter;

    public View onCreateView
            (@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.fragment_login1, container, false);
        //id注册
        listView = view.findViewById(R.id.lg3_listView);
        MarqueTextView as = view.findViewById(R.id.mtv);
        simpleAdapter = new SimpleAdapter(getActivity(),getData(),R.layout.item,
                new String[]{"title","image"},new int[]{R.id.item_tv,R.id.item_image});
        //设置适配器监听器
        listView.setAdapter(simpleAdapter);
        listView.setOnItemClickListener(this);
        //将文件由assets转移到手机空间
        copyFilesFromAssets(getActivity(),"ml","/data/data/com.example.ebook/files");
        as.setText(load());
        return view;
    }

    //设置listview图文
    private List<Map<String,Object>> getData() {
        String [] titles={"射雕英雄传\n简介:南宋末年,金兵入侵,朝廷奸臣当道。全真派丘处机因杀死汉奸王道乾,被官兵追杀,逃到江南一个小村牛家庄,与隐居在此处的爱国义士郭啸天和杨铁心一见如故,以“靖、康”为他俩即将出生的孩子命名,并留下一对短剑作为信物……",
                "神雕侠侣\n简介:南宋末年,江南少年杨过被郭靖送去全真教学武。全真教教规森严,天性叛逆的杨过在教中吃尽苦头,忍无可忍,终于逃出全真教。被活死人墓中的小龙女收留为徒。师徒二人在墓中一起练武、一起长大,渐生情愫……",
                "倚天屠龙记\n简介:少林寺觉远禅师看护《楞伽经》不力,导致经书被尹克西和潇湘子盗走。潇湘子和尹克西互相猜忌残杀,临终之前幡然醒悟,委托何足道向觉远转达经书的下落。何足道接受委托,并向少林寺发出挑战。适逢郭靖、黄蓉的女儿郭襄前往少林寺寻找杨过……",
                "绝对娇宠\n简介:作者:陈三年\n斯文霸道总裁装逼乘骄纵美人,爱让人勇敢,双向奔赴,梦会成真。",
                "斗罗大陆\n简介:外门弟子唐三,因偷学内门绝学为唐门所不容,跳崖明志时却发专现没有死,反而以另属外一个身份来到了另一个世界,名叫斗罗大陆。这里没有魔法,没有斗气,没有武术,却有神奇的武魂……",
                "末日游戏场\n简介:系统:“你们的任务是活下去,倾尽所有,不择手段地活下去,完成一场挑战,即可获得一张通行证。\n星际333年,地球环境无法生存 开启全民末日游戏场,提过游戏得到通行证,获取生存资格。",
                "完美世界\n简介:一粒尘可填海,一根草斩尽日月星辰,弹指间天翻地覆。\n群雄并起,万族林立,诸圣争霸,乱天动地。问苍茫大地,谁主沉浮?!\n一个少年从大荒中走出,一切从这里开始\n"};
        int [] images={R.drawable.im1,R.drawable.im2,R.drawable.im3,
                R.drawable.im4,R.drawable.im5,
                R.drawable.im6,R.drawable.im7};
        List<Map<String,Object>> list= new ArrayList<>();
        for(int i=0;i<7;i++){
            Map  map = new HashMap();
            map.put("title",titles[i]);
            map.put("image",images[i]);
            list.add(map);
        }
        return list;
    }

    public void onActivityCreated(@Nullable Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);

    }
    //设置listview点击触发事件
    @Override
    public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
        switch (position){
            case 0:
                Intent intent0 = new Intent(getActivity(),ReadBook.class);
                intent0.putExtra("extra","0.txt");
                startActivity(intent0);
                break;
            case 1:
                Intent intent1 = new Intent(getActivity(),ReadBook.class);
                intent1.putExtra("extra","1.txt");
                startActivity(intent1);
                break;
            case 2:
                Intent intent2 = new Intent(getActivity(),ReadBook.class);
                intent2.putExtra("extra","2.txt");
                startActivity(intent2);
                break;
            case 3:
                Intent intent3 = new Intent(getActivity(),ReadBook.class);
                intent3.putExtra("extra","3.txt");
                startActivity(intent3);
                break;
            case 4:
                Intent intent4 = new Intent(getActivity(),ReadBook.class);
                intent4.putExtra("extra","4.txt");
                startActivity(intent4);
                break;
            case 5:
                Intent intent5 = new Intent(getActivity(),ReadBook.class);
                intent5.putExtra("extra","5.txt");
                startActivity(intent5);
                break;
            case 6:
                Intent intent6 = new Intent(getActivity(),ReadBook.class);
                intent6.putExtra("extra","6.txt");
                startActivity(intent6);
                break;

            default:
                throw new IllegalStateException("Unexpected value: " + position);


        }


    }



//       从assets目录中复制整个文件夹内容到新的路径下
//       @param  context  Context 使用CopyFiles类的Activity
//      @param  oldPath  String  原文件路径  如:Data(assets文件夹下文件夹名称)
//      @param  newPath  String  复制后路径  如:data/data/(手机内部存储路径名称)

    public void copyFilesFromAssets(Context context, String oldPath, String newPath) {
        try {
            String fileNames[] = context.getAssets().list(oldPath);//获取assets目录下的所有文件及目录名
            if (fileNames.length > 0) {//如果是目录
                File file = new File(newPath);
                file.mkdirs();//如果文件夹不存在,则递归
                for (String fileName : fileNames) {
                    copyFilesFromAssets(context,oldPath + "/" + fileName,newPath+"/"+fileName);
                }
            } else {//如果是文件
                InputStream is = context.getAssets().open(oldPath);
                FileOutputStream fos = new FileOutputStream(new File(newPath));
                byte[] buffer = new byte[1024];
                int byteCount=0;
                while((byteCount=is.read(buffer))!=-1) {//循环从输入流读取 buffer字节
                    fos.write(buffer, 0, byteCount);//将读取的输入流写入到输出流
                }
                fos.flush();//刷新缓冲区
                is.close();
                fos.close();
            }
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
            //如果捕捉到错误则通知UI线程
            //MainActivity.handler.sendEmptyMessage(COPY_FALSE);
        }


    }

    //设置顶部文字并循环播放
    String  load(){
        String tit="  欢迎来到Ebook阅读器,这是您打开知识宝库的钥匙!\n" +
                "  欢迎来到Ebook阅读器,这是您打开知识宝库的钥匙!\n" ;
        return tit;
    }
}

其实现在看来当时写的还是非常简单的,在书架展示这一块是靠代码后台输入文字介绍,以及用简单地Switch现实跳转,换成现在有更好的解决方式。不过这是后话,我想能力的提升也是循序渐进的。我还是来解释一些核心的代码。
书的内容是以txt格式存放在之前提到的asset文件夹里面,copyFilesFromAssets函数便是实现对asset文件内容的读取。这样就实现了文件可以随着代码一起保存并放到手机上,不用创建数据库(现在觉得创建了数据库实现的功能可能可以更加完善)是一种投机取巧的方式
这里的适配器simpleAdapter里的item:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="horizontal"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <ImageView
        android:id="@+id/item_image"
        android:layout_margin="10dp"
        android:layout_width="280px"
        android:layout_height="580px" />

    <TextView
        android:textSize="15dp"
        android:layout_margin="10dp"
        android:padding="10px"
        android:textColor="@color/black"
        android:layout_width="match_parent"
        android:layout_height="580px"
        android:id="@+id/item_tv"/>


</LinearLayout>

当用户点进一本书后会看到类似界面:
请添加图片描述
activity_read_book.xml:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:background="@drawable/rbookbg3"
    tools:context=".ReadBook">
    <TextView
        android:layout_width="wrap_content"
        android:layout_height="650dp"
        android:textSize="20dp"
        android:id="@+id/sd"
        android:layout_margin="30dp"
        android:textColor="@color/black"
        android:scrollbars="vertical"
        />

</LinearLayout>

java文件ReadBook:

public class ReadBook extends AppCompatActivity {
    String txt;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_read_book);

        Intent intent=getIntent();
        txt=intent.getStringExtra("extra");

        Log.d("ReadBook",txt);

        TextView sb =  findViewById(R.id.sd);
        sb.setMovementMethod(ScrollingMovementMethod.getInstance());
        sb.setSelected(true);
        sb.setText(load());
    }


    //获取手机空间中的txt文件
    public String load() {
        FileInputStream in = null;
        BufferedReader reader = null;
        StringBuilder content = new StringBuilder();
        try {
            in = openFileInput(txt);
            reader = new BufferedReader(new InputStreamReader(in));
            String line = "";
            while ((line = reader.readLine()) != null) {
                content.append("\n");
                content.append(line);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (reader != null) {
                try {
                    reader.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return content.toString();
    }
}

这里的Log是用来实现两个活动直接的信息传递,load函数就是基本的读取txt文件操作了。

到此为止作为一个阅读器最重要的阅读功能就可以实现了,接下来的感悟和分类,均使用到了数据库,待下回分解。

  • 5
    点赞
  • 48
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 5
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

睨箐

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值